refactor: 管理端去租户过滤并Portal化RBAC菜单
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using TakeoutSaaS.Application.App.Stores;
|
||||
using TakeoutSaaS.Application.App.Stores.Commands;
|
||||
@@ -9,7 +8,6 @@ using TakeoutSaaS.Domain.Stores.Entities;
|
||||
using TakeoutSaaS.Domain.Stores.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -18,8 +16,6 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// </summary>
|
||||
public sealed class BatchUpdateBusinessHoursCommandHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<BatchUpdateBusinessHoursCommandHandler> logger)
|
||||
: IRequestHandler<BatchUpdateBusinessHoursCommand, IReadOnlyList<StoreBusinessHourDto>>
|
||||
{
|
||||
@@ -27,9 +23,7 @@ public sealed class BatchUpdateBusinessHoursCommandHandler(
|
||||
public async Task<IReadOnlyList<StoreBusinessHourDto>> Handle(BatchUpdateBusinessHoursCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验门店存在
|
||||
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, null, cancellationToken);
|
||||
if (store is null)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.NotFound, "门店不存在");
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using TakeoutSaaS.Application.App.Stores.Dto;
|
||||
using TakeoutSaaS.Application.App.Stores.Queries;
|
||||
using TakeoutSaaS.Application.App.Stores.Services;
|
||||
@@ -8,7 +7,6 @@ using TakeoutSaaS.Domain.Stores.Enums;
|
||||
using TakeoutSaaS.Domain.Stores.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -17,8 +15,6 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// </summary>
|
||||
public sealed class CalculateStoreFeeQueryHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IStoreFeeCalculationService feeCalculationService)
|
||||
: IRequestHandler<CalculateStoreFeeQuery, StoreFeeCalculationResultDto>
|
||||
{
|
||||
@@ -26,16 +22,14 @@ public sealed class CalculateStoreFeeQueryHandler(
|
||||
public async Task<StoreFeeCalculationResultDto> Handle(CalculateStoreFeeQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验门店存在
|
||||
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, null, cancellationToken);
|
||||
if (store is null)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.NotFound, "门店不存在");
|
||||
}
|
||||
|
||||
// 2. (空行后) 获取费用配置
|
||||
var fee = await storeRepository.GetStoreFeeAsync(request.StoreId, tenantId, cancellationToken)
|
||||
var fee = await storeRepository.GetStoreFeeAsync(request.StoreId, null, cancellationToken)
|
||||
?? new StoreFee
|
||||
{
|
||||
StoreId = request.StoreId,
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
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.Application.App.Stores.Services;
|
||||
using TakeoutSaaS.Domain.Stores.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -16,8 +13,6 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// </summary>
|
||||
public sealed class CheckStoreDeliveryZoneQueryHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IDeliveryZoneService deliveryZoneService)
|
||||
: IRequestHandler<CheckStoreDeliveryZoneQuery, StoreDeliveryCheckResultDto>
|
||||
{
|
||||
@@ -25,16 +20,14 @@ public sealed class CheckStoreDeliveryZoneQueryHandler(
|
||||
public async Task<StoreDeliveryCheckResultDto> Handle(CheckStoreDeliveryZoneQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验门店存在
|
||||
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, null, cancellationToken);
|
||||
if (store is null)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.NotFound, "门店不存在");
|
||||
}
|
||||
|
||||
// 2. (空行后) 执行配送范围判断
|
||||
var zones = await storeRepository.GetDeliveryZonesAsync(request.StoreId, tenantId, cancellationToken);
|
||||
var zones = await storeRepository.GetDeliveryZonesAsync(request.StoreId, null, cancellationToken);
|
||||
var result = deliveryZoneService.CheckPointInZones(zones, request.Longitude, request.Latitude);
|
||||
|
||||
// 3. (空行后) 计算距离
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using TakeoutSaaS.Application.App.Stores.Dto;
|
||||
using TakeoutSaaS.Application.App.Stores.Queries;
|
||||
using TakeoutSaaS.Domain.Stores.Enums;
|
||||
using TakeoutSaaS.Domain.Stores.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -14,18 +12,14 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// 门店资质完整性检查处理器。
|
||||
/// </summary>
|
||||
public sealed class CheckStoreQualificationsQueryHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
IStoreRepository storeRepository)
|
||||
: IRequestHandler<CheckStoreQualificationsQuery, StoreQualificationCheckResultDto>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<StoreQualificationCheckResultDto> Handle(CheckStoreQualificationsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验门店存在
|
||||
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, null, cancellationToken);
|
||||
if (store is null)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.NotFound, "门店不存在");
|
||||
@@ -42,7 +36,7 @@ public sealed class CheckStoreQualificationsQueryHandler(
|
||||
}
|
||||
|
||||
// 3. (空行后) 读取资质列表并统计
|
||||
var qualifications = await storeRepository.GetQualificationsAsync(request.StoreId, tenantId, cancellationToken);
|
||||
var qualifications = await storeRepository.GetQualificationsAsync(request.StoreId, null, cancellationToken);
|
||||
var grouped = qualifications
|
||||
.GroupBy(x => x.QualificationType)
|
||||
.ToDictionary(x => x.Key, x => x.ToList());
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
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.Merchants.Repositories;
|
||||
@@ -10,7 +8,6 @@ using TakeoutSaaS.Domain.Stores.Enums;
|
||||
using TakeoutSaaS.Domain.Stores.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -20,8 +17,6 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
public sealed class CreateStoreCommandHandler(
|
||||
IStoreRepository storeRepository,
|
||||
IMerchantRepository merchantRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<CreateStoreCommandHandler> logger)
|
||||
: IRequestHandler<CreateStoreCommand, StoreDto>
|
||||
{
|
||||
@@ -29,11 +24,7 @@ public sealed class CreateStoreCommandHandler(
|
||||
public async Task<StoreDto> Handle(CreateStoreCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 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);
|
||||
var merchant = await merchantRepository.FindByIdAsync(request.MerchantId, cancellationToken);
|
||||
if (merchant == null)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.NotFound, "商户不存在");
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using TakeoutSaaS.Application.App.Stores;
|
||||
using TakeoutSaaS.Application.App.Stores.Commands;
|
||||
@@ -9,7 +8,6 @@ using TakeoutSaaS.Domain.Stores.Entities;
|
||||
using TakeoutSaaS.Domain.Stores.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -18,8 +16,6 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// </summary>
|
||||
public sealed class CreateStoreDeliveryZoneCommandHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IGeoJsonValidationService geoJsonValidationService,
|
||||
ILogger<CreateStoreDeliveryZoneCommandHandler> logger)
|
||||
: IRequestHandler<CreateStoreDeliveryZoneCommand, StoreDeliveryZoneDto>
|
||||
@@ -28,9 +24,7 @@ public sealed class CreateStoreDeliveryZoneCommandHandler(
|
||||
public async Task<StoreDeliveryZoneDto> Handle(CreateStoreDeliveryZoneCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验门店存在
|
||||
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, null, cancellationToken);
|
||||
if (store is null)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.NotFound, "门店不存在");
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using TakeoutSaaS.Application.App.Stores;
|
||||
using TakeoutSaaS.Application.App.Stores.Commands;
|
||||
@@ -9,7 +8,6 @@ using TakeoutSaaS.Domain.Stores.Enums;
|
||||
using TakeoutSaaS.Domain.Stores.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -18,23 +16,14 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// </summary>
|
||||
public sealed class CreateStoreHolidayCommandHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<CreateStoreHolidayCommandHandler> logger)
|
||||
: IRequestHandler<CreateStoreHolidayCommand, StoreHolidayDto>
|
||||
{
|
||||
private readonly IStoreRepository _storeRepository = storeRepository;
|
||||
private readonly ITenantProvider _tenantProvider = tenantProvider;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
|
||||
private readonly ILogger<CreateStoreHolidayCommandHandler> _logger = logger;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<StoreHolidayDto> Handle(CreateStoreHolidayCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验门店存在
|
||||
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, null, cancellationToken);
|
||||
if (store is null)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.NotFound, "门店不存在");
|
||||
@@ -57,9 +46,9 @@ public sealed class CreateStoreHolidayCommandHandler(
|
||||
};
|
||||
|
||||
// 3. 持久化
|
||||
await _storeRepository.AddHolidaysAsync(new[] { holiday }, cancellationToken);
|
||||
await _storeRepository.SaveChangesAsync(cancellationToken);
|
||||
_logger.LogInformation("创建节假日 {HolidayId} 对应门店 {StoreId}", holiday.Id, request.StoreId);
|
||||
await storeRepository.AddHolidaysAsync(new[] { holiday }, cancellationToken);
|
||||
await storeRepository.SaveChangesAsync(cancellationToken);
|
||||
logger.LogInformation("创建节假日 {HolidayId} 对应门店 {StoreId}", holiday.Id, request.StoreId);
|
||||
|
||||
// 4. 返回 DTO
|
||||
return StoreMapping.ToDto(holiday);
|
||||
|
||||
@@ -1,10 +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;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -13,23 +10,14 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// </summary>
|
||||
public sealed class DeleteStoreDeliveryZoneCommandHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<DeleteStoreDeliveryZoneCommandHandler> logger)
|
||||
: IRequestHandler<DeleteStoreDeliveryZoneCommand, bool>
|
||||
{
|
||||
private readonly IStoreRepository _storeRepository = storeRepository;
|
||||
private readonly ITenantProvider _tenantProvider = tenantProvider;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
|
||||
private readonly ILogger<DeleteStoreDeliveryZoneCommandHandler> _logger = logger;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> Handle(DeleteStoreDeliveryZoneCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 读取区域
|
||||
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(_httpContextAccessor);
|
||||
var tenantId = ignoreTenantFilter ? 0 : _tenantProvider.GetCurrentTenantId();
|
||||
var existing = await _storeRepository.FindDeliveryZoneByIdAsync(request.DeliveryZoneId, tenantId, cancellationToken);
|
||||
var existing = await storeRepository.FindDeliveryZoneByIdAsync(request.DeliveryZoneId, null, cancellationToken);
|
||||
if (existing is null)
|
||||
{
|
||||
return false;
|
||||
@@ -42,9 +30,9 @@ public sealed class DeleteStoreDeliveryZoneCommandHandler(
|
||||
}
|
||||
|
||||
// 3. 删除
|
||||
await _storeRepository.DeleteDeliveryZoneAsync(request.DeliveryZoneId, tenantId, cancellationToken);
|
||||
await _storeRepository.SaveChangesAsync(cancellationToken);
|
||||
_logger.LogInformation("删除配送区域 {DeliveryZoneId} 对应门店 {StoreId}", request.DeliveryZoneId, request.StoreId);
|
||||
await storeRepository.DeleteDeliveryZoneAsync(request.DeliveryZoneId, null, cancellationToken);
|
||||
await storeRepository.SaveChangesAsync(cancellationToken);
|
||||
logger.LogInformation("删除配送区域 {DeliveryZoneId} 对应门店 {StoreId}", request.DeliveryZoneId, request.StoreId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,10 +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;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -13,23 +10,14 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// </summary>
|
||||
public sealed class DeleteStoreHolidayCommandHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<DeleteStoreHolidayCommandHandler> logger)
|
||||
: IRequestHandler<DeleteStoreHolidayCommand, bool>
|
||||
{
|
||||
private readonly IStoreRepository _storeRepository = storeRepository;
|
||||
private readonly ITenantProvider _tenantProvider = tenantProvider;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
|
||||
private readonly ILogger<DeleteStoreHolidayCommandHandler> _logger = logger;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> Handle(DeleteStoreHolidayCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 读取配置
|
||||
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(_httpContextAccessor);
|
||||
var tenantId = ignoreTenantFilter ? 0 : _tenantProvider.GetCurrentTenantId();
|
||||
var existing = await _storeRepository.FindHolidayByIdAsync(request.HolidayId, tenantId, cancellationToken);
|
||||
var existing = await storeRepository.FindHolidayByIdAsync(request.HolidayId, null, cancellationToken);
|
||||
if (existing is null)
|
||||
{
|
||||
return false;
|
||||
@@ -42,9 +30,9 @@ public sealed class DeleteStoreHolidayCommandHandler(
|
||||
}
|
||||
|
||||
// 3. 删除
|
||||
await _storeRepository.DeleteHolidayAsync(request.HolidayId, tenantId, cancellationToken);
|
||||
await _storeRepository.SaveChangesAsync(cancellationToken);
|
||||
_logger.LogInformation("删除节假日 {HolidayId} 对应门店 {StoreId}", request.HolidayId, request.StoreId);
|
||||
await storeRepository.DeleteHolidayAsync(request.HolidayId, null, cancellationToken);
|
||||
await storeRepository.SaveChangesAsync(cancellationToken);
|
||||
logger.LogInformation("删除节假日 {HolidayId} 对应门店 {StoreId}", request.HolidayId, request.StoreId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
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.Entities;
|
||||
using TakeoutSaaS.Domain.Stores.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -13,17 +11,14 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// 门店详情查询处理器。
|
||||
/// </summary>
|
||||
public sealed class GetStoreByIdQueryHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
IStoreRepository storeRepository)
|
||||
: IRequestHandler<GetStoreByIdQuery, StoreDto?>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<StoreDto?> Handle(GetStoreByIdQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
|
||||
var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId();
|
||||
var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken);
|
||||
// 1. 查询门店详情(默认跨租户,支持包含软删除)
|
||||
var store = await storeRepository.FindByIdAsync(request.StoreId, null, cancellationToken, includeDeleted: request.IncludeDeleted);
|
||||
return store == null ? null : StoreMapping.ToDto(store);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
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.Entities;
|
||||
using TakeoutSaaS.Domain.Stores.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -14,25 +13,21 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// 获取门店费用配置处理器。
|
||||
/// </summary>
|
||||
public sealed class GetStoreFeeQueryHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
IStoreRepository storeRepository)
|
||||
: IRequestHandler<GetStoreFeeQuery, StoreFeeDto?>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<StoreFeeDto?> Handle(GetStoreFeeQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验门店存在
|
||||
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, null, cancellationToken);
|
||||
if (store is null)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.NotFound, "门店不存在");
|
||||
}
|
||||
|
||||
// 2. (空行后) 查询费用配置
|
||||
var fee = await storeRepository.GetStoreFeeAsync(request.StoreId, tenantId, cancellationToken);
|
||||
var fee = await storeRepository.GetStoreFeeAsync(request.StoreId, null, cancellationToken);
|
||||
if (fee is null)
|
||||
{
|
||||
var fallback = new StoreFee
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
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;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -12,24 +11,16 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// 营业时段列表查询处理器。
|
||||
/// </summary>
|
||||
public sealed class ListStoreBusinessHoursQueryHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
IStoreRepository storeRepository)
|
||||
: IRequestHandler<ListStoreBusinessHoursQuery, IReadOnlyList<StoreBusinessHourDto>>
|
||||
{
|
||||
private readonly IStoreRepository _storeRepository = storeRepository;
|
||||
private readonly ITenantProvider _tenantProvider = tenantProvider;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<StoreBusinessHourDto>> Handle(ListStoreBusinessHoursQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 查询时段列表
|
||||
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, null, cancellationToken);
|
||||
|
||||
// 2. 映射 DTO
|
||||
// 2. (空行后) 映射 DTO
|
||||
return hours.Select(StoreMapping.ToDto).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
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;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -12,24 +11,16 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// 配送区域列表查询处理器。
|
||||
/// </summary>
|
||||
public sealed class ListStoreDeliveryZonesQueryHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
IStoreRepository storeRepository)
|
||||
: IRequestHandler<ListStoreDeliveryZonesQuery, IReadOnlyList<StoreDeliveryZoneDto>>
|
||||
{
|
||||
private readonly IStoreRepository _storeRepository = storeRepository;
|
||||
private readonly ITenantProvider _tenantProvider = tenantProvider;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<StoreDeliveryZoneDto>> Handle(ListStoreDeliveryZonesQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 查询配送区域
|
||||
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, null, cancellationToken);
|
||||
|
||||
// 2. 映射 DTO
|
||||
// 2. (空行后) 映射 DTO
|
||||
return zones.Select(StoreMapping.ToDto).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
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;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -13,24 +11,16 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// 门店节假日列表查询处理器。
|
||||
/// </summary>
|
||||
public sealed class ListStoreHolidaysQueryHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
IStoreRepository storeRepository)
|
||||
: IRequestHandler<ListStoreHolidaysQuery, IReadOnlyList<StoreHolidayDto>>
|
||||
{
|
||||
private readonly IStoreRepository _storeRepository = storeRepository;
|
||||
private readonly ITenantProvider _tenantProvider = tenantProvider;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<StoreHolidayDto>> Handle(ListStoreHolidaysQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 查询节假日
|
||||
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(_httpContextAccessor);
|
||||
var tenantId = ignoreTenantFilter ? 0 : _tenantProvider.GetCurrentTenantId();
|
||||
var holidays = await _storeRepository.GetHolidaysAsync(request.StoreId, tenantId, cancellationToken);
|
||||
var holidays = await storeRepository.GetHolidaysAsync(request.StoreId, null, cancellationToken);
|
||||
|
||||
// 2. 映射 DTO
|
||||
// 2. (空行后) 映射 DTO
|
||||
return holidays.Select(StoreMapping.ToDto).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
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;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -13,25 +12,21 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// 门店资质列表查询处理器。
|
||||
/// </summary>
|
||||
public sealed class ListStoreQualificationsQueryHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
IStoreRepository storeRepository)
|
||||
: IRequestHandler<ListStoreQualificationsQuery, IReadOnlyList<StoreQualificationDto>>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<StoreQualificationDto>> Handle(ListStoreQualificationsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验门店存在
|
||||
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, null, cancellationToken);
|
||||
if (store is null)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.NotFound, "门店不存在");
|
||||
}
|
||||
|
||||
// 2. (空行后) 读取资质列表
|
||||
var qualifications = await storeRepository.GetQualificationsAsync(request.StoreId, tenantId, cancellationToken);
|
||||
var qualifications = await storeRepository.GetQualificationsAsync(request.StoreId, null, cancellationToken);
|
||||
|
||||
// 3. (空行后) 映射 DTO
|
||||
return qualifications.Select(StoreMapping.ToDto).ToList();
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
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;
|
||||
using TakeoutSaaS.Shared.Abstractions.Results;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -13,33 +11,32 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// 门店列表查询处理器。
|
||||
/// </summary>
|
||||
public sealed class SearchStoresQueryHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
IStoreRepository storeRepository)
|
||||
: IRequestHandler<SearchStoresQuery, PagedResult<StoreDto>>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<PagedResult<StoreDto>> Handle(SearchStoresQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
|
||||
var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId();
|
||||
// 1. 查询门店列表(可选租户过滤)
|
||||
var stores = await storeRepository.SearchAsync(
|
||||
tenantId,
|
||||
request.TenantId,
|
||||
request.MerchantId,
|
||||
request.Status,
|
||||
request.AuditStatus,
|
||||
request.BusinessStatus,
|
||||
request.OwnershipType,
|
||||
request.Keyword,
|
||||
ignoreTenantFilter,
|
||||
request.IncludeDeleted,
|
||||
cancellationToken);
|
||||
|
||||
// 2. (空行后) 排序与分页
|
||||
var sorted = ApplySorting(stores, request.SortBy, request.SortDescending);
|
||||
var paged = sorted
|
||||
.Skip((request.Page - 1) * request.PageSize)
|
||||
.Take(request.PageSize)
|
||||
.ToList();
|
||||
|
||||
// 3. (空行后) 映射 DTO
|
||||
var items = paged.Select(StoreMapping.ToDto).ToList();
|
||||
return new PagedResult<StoreDto>(items, request.Page, request.PageSize, stores.Count);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
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;
|
||||
using TakeoutSaaS.Domain.Stores.Entities;
|
||||
using TakeoutSaaS.Domain.Stores.Enums;
|
||||
using TakeoutSaaS.Domain.Stores.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -18,8 +16,6 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// </summary>
|
||||
public sealed class UpdateStoreCommandHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<UpdateStoreCommandHandler> logger)
|
||||
: IRequestHandler<UpdateStoreCommand, StoreDto?>
|
||||
{
|
||||
@@ -27,9 +23,7 @@ public sealed class UpdateStoreCommandHandler(
|
||||
public async Task<StoreDto?> Handle(UpdateStoreCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 读取门店
|
||||
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
|
||||
var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId();
|
||||
var existing = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken);
|
||||
var existing = await storeRepository.FindByIdAsync(request.StoreId, null, cancellationToken);
|
||||
if (existing == null)
|
||||
{
|
||||
return null;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using TakeoutSaaS.Application.App.Stores;
|
||||
using TakeoutSaaS.Application.App.Stores.Commands;
|
||||
@@ -9,7 +8,6 @@ using TakeoutSaaS.Domain.Stores.Entities;
|
||||
using TakeoutSaaS.Domain.Stores.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -18,8 +16,6 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// </summary>
|
||||
public sealed class UpdateStoreDeliveryZoneCommandHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IGeoJsonValidationService geoJsonValidationService,
|
||||
ILogger<UpdateStoreDeliveryZoneCommandHandler> logger)
|
||||
: IRequestHandler<UpdateStoreDeliveryZoneCommand, StoreDeliveryZoneDto?>
|
||||
@@ -28,9 +24,7 @@ public sealed class UpdateStoreDeliveryZoneCommandHandler(
|
||||
public async Task<StoreDeliveryZoneDto?> Handle(UpdateStoreDeliveryZoneCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 读取区域
|
||||
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
|
||||
var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId();
|
||||
var existing = await storeRepository.FindDeliveryZoneByIdAsync(request.DeliveryZoneId, tenantId, cancellationToken);
|
||||
var existing = await storeRepository.FindDeliveryZoneByIdAsync(request.DeliveryZoneId, null, cancellationToken);
|
||||
if (existing is null)
|
||||
{
|
||||
return null;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using TakeoutSaaS.Application.App.Stores;
|
||||
using TakeoutSaaS.Application.App.Stores.Commands;
|
||||
@@ -9,7 +8,6 @@ using TakeoutSaaS.Domain.Stores.Enums;
|
||||
using TakeoutSaaS.Domain.Stores.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -18,8 +16,6 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// </summary>
|
||||
public sealed class UpdateStoreFeeCommandHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<UpdateStoreFeeCommandHandler> logger)
|
||||
: IRequestHandler<UpdateStoreFeeCommand, StoreFeeDto>
|
||||
{
|
||||
@@ -27,9 +23,7 @@ public sealed class UpdateStoreFeeCommandHandler(
|
||||
public async Task<StoreFeeDto> Handle(UpdateStoreFeeCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验门店状态
|
||||
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, null, cancellationToken);
|
||||
if (store is null)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.NotFound, "门店不存在");
|
||||
@@ -45,7 +39,7 @@ public sealed class UpdateStoreFeeCommandHandler(
|
||||
}
|
||||
|
||||
// 2. (空行后) 获取或创建费用配置
|
||||
var fee = await storeRepository.GetStoreFeeAsync(request.StoreId, tenantId, cancellationToken);
|
||||
var fee = await storeRepository.GetStoreFeeAsync(request.StoreId, null, cancellationToken);
|
||||
var isNew = fee is null;
|
||||
fee ??= new StoreFee
|
||||
{
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
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;
|
||||
using TakeoutSaaS.Domain.Stores.Enums;
|
||||
using TakeoutSaaS.Domain.Stores.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
|
||||
@@ -18,23 +15,14 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
|
||||
/// </summary>
|
||||
public sealed class UpdateStoreHolidayCommandHandler(
|
||||
IStoreRepository storeRepository,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<UpdateStoreHolidayCommandHandler> logger)
|
||||
: IRequestHandler<UpdateStoreHolidayCommand, StoreHolidayDto?>
|
||||
{
|
||||
private readonly IStoreRepository _storeRepository = storeRepository;
|
||||
private readonly ITenantProvider _tenantProvider = tenantProvider;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
|
||||
private readonly ILogger<UpdateStoreHolidayCommandHandler> _logger = logger;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<StoreHolidayDto?> Handle(UpdateStoreHolidayCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 读取配置
|
||||
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(_httpContextAccessor);
|
||||
var tenantId = ignoreTenantFilter ? 0 : _tenantProvider.GetCurrentTenantId();
|
||||
var existing = await _storeRepository.FindHolidayByIdAsync(request.HolidayId, tenantId, cancellationToken);
|
||||
var existing = await storeRepository.FindHolidayByIdAsync(request.HolidayId, null, cancellationToken);
|
||||
if (existing is null)
|
||||
{
|
||||
return null;
|
||||
@@ -57,9 +45,9 @@ public sealed class UpdateStoreHolidayCommandHandler(
|
||||
existing.Reason = request.Reason?.Trim();
|
||||
|
||||
// 4. 持久化
|
||||
await _storeRepository.UpdateHolidayAsync(existing, cancellationToken);
|
||||
await _storeRepository.SaveChangesAsync(cancellationToken);
|
||||
_logger.LogInformation("更新节假日 {HolidayId} 对应门店 {StoreId}", existing.Id, existing.StoreId);
|
||||
await storeRepository.UpdateHolidayAsync(existing, cancellationToken);
|
||||
await storeRepository.SaveChangesAsync(cancellationToken);
|
||||
logger.LogInformation("更新节假日 {HolidayId} 对应门店 {StoreId}", existing.Id, existing.StoreId);
|
||||
|
||||
// 5. 返回 DTO
|
||||
return StoreMapping.ToDto(existing);
|
||||
|
||||
@@ -12,4 +12,9 @@ public sealed class GetStoreByIdQuery : IRequest<StoreDto?>
|
||||
/// 门店 ID。
|
||||
/// </summary>
|
||||
public long StoreId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含已软删除数据。
|
||||
/// </summary>
|
||||
public bool IncludeDeleted { get; init; }
|
||||
}
|
||||
|
||||
@@ -10,6 +10,11 @@ namespace TakeoutSaaS.Application.App.Stores.Queries;
|
||||
/// </summary>
|
||||
public sealed class SearchStoresQuery : IRequest<PagedResult<StoreDto>>
|
||||
{
|
||||
/// <summary>
|
||||
/// 租户 ID(为空则查询全部租户)。
|
||||
/// </summary>
|
||||
public long? TenantId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 商户 ID(可选)。
|
||||
/// </summary>
|
||||
@@ -40,6 +45,11 @@ public sealed class SearchStoresQuery : IRequest<PagedResult<StoreDto>>
|
||||
/// </summary>
|
||||
public string? Keyword { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含已软删除数据。
|
||||
/// </summary>
|
||||
public bool IncludeDeleted { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 页码。
|
||||
/// </summary>
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Text.Json;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Commands;
|
||||
@@ -15,7 +14,6 @@ namespace TakeoutSaaS.Application.App.Subscriptions.Handlers;
|
||||
/// </summary>
|
||||
public sealed class BatchExtendSubscriptionsCommandHandler(
|
||||
ISubscriptionRepository subscriptionRepository,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IIdGenerator idGenerator,
|
||||
ILogger<BatchExtendSubscriptionsCommandHandler> logger)
|
||||
: IRequestHandler<BatchExtendSubscriptionsCommand, BatchExtendResult>
|
||||
@@ -23,8 +21,6 @@ public sealed class BatchExtendSubscriptionsCommandHandler(
|
||||
/// <inheritdoc />
|
||||
public async Task<BatchExtendResult> Handle(BatchExtendSubscriptionsCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var ignoreTenantFilter = SubscriptionTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
|
||||
|
||||
var successCount = 0;
|
||||
var failures = new List<BatchFailureItem>();
|
||||
|
||||
@@ -41,8 +37,7 @@ public sealed class BatchExtendSubscriptionsCommandHandler(
|
||||
// 查询所有订阅
|
||||
var subscriptions = await subscriptionRepository.FindByIdsAsync(
|
||||
request.SubscriptionIds,
|
||||
cancellationToken,
|
||||
ignoreTenantFilter: ignoreTenantFilter);
|
||||
cancellationToken);
|
||||
|
||||
foreach (var subscriptionId in request.SubscriptionIds)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Text.Json;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Commands;
|
||||
@@ -15,7 +14,6 @@ namespace TakeoutSaaS.Application.App.Subscriptions.Handlers;
|
||||
/// </summary>
|
||||
public sealed class BatchSendReminderCommandHandler(
|
||||
ISubscriptionRepository subscriptionRepository,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IIdGenerator idGenerator,
|
||||
ILogger<BatchSendReminderCommandHandler> logger)
|
||||
: IRequestHandler<BatchSendReminderCommand, BatchSendReminderResult>
|
||||
@@ -23,16 +21,13 @@ public sealed class BatchSendReminderCommandHandler(
|
||||
/// <inheritdoc />
|
||||
public async Task<BatchSendReminderResult> Handle(BatchSendReminderCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var ignoreTenantFilter = SubscriptionTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
|
||||
|
||||
var successCount = 0;
|
||||
var failures = new List<BatchFailureItem>();
|
||||
|
||||
// 查询所有订阅及租户信息
|
||||
var subscriptions = await subscriptionRepository.FindByIdsWithTenantAsync(
|
||||
request.SubscriptionIds,
|
||||
cancellationToken,
|
||||
ignoreTenantFilter: ignoreTenantFilter);
|
||||
cancellationToken);
|
||||
|
||||
foreach (var subscriptionId in request.SubscriptionIds)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Commands;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Dto;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Queries;
|
||||
@@ -15,7 +14,6 @@ namespace TakeoutSaaS.Application.App.Subscriptions.Handlers;
|
||||
/// </summary>
|
||||
public sealed class ChangeSubscriptionPlanCommandHandler(
|
||||
ISubscriptionRepository subscriptionRepository,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IIdGenerator idGenerator,
|
||||
IMediator mediator)
|
||||
: IRequestHandler<ChangeSubscriptionPlanCommand, SubscriptionDetailDto?>
|
||||
@@ -23,13 +21,10 @@ public sealed class ChangeSubscriptionPlanCommandHandler(
|
||||
/// <inheritdoc />
|
||||
public async Task<SubscriptionDetailDto?> Handle(ChangeSubscriptionPlanCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var ignoreTenantFilter = SubscriptionTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
|
||||
|
||||
// 1. 查询订阅
|
||||
var subscription = await subscriptionRepository.FindByIdAsync(
|
||||
request.SubscriptionId,
|
||||
cancellationToken,
|
||||
ignoreTenantFilter: ignoreTenantFilter);
|
||||
cancellationToken);
|
||||
|
||||
if (subscription == null)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Commands;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Dto;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Queries;
|
||||
@@ -15,7 +14,6 @@ namespace TakeoutSaaS.Application.App.Subscriptions.Handlers;
|
||||
/// </summary>
|
||||
public sealed class ExtendSubscriptionCommandHandler(
|
||||
ISubscriptionRepository subscriptionRepository,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IIdGenerator idGenerator,
|
||||
IMediator mediator)
|
||||
: IRequestHandler<ExtendSubscriptionCommand, SubscriptionDetailDto?>
|
||||
@@ -23,13 +21,10 @@ public sealed class ExtendSubscriptionCommandHandler(
|
||||
/// <inheritdoc />
|
||||
public async Task<SubscriptionDetailDto?> Handle(ExtendSubscriptionCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var ignoreTenantFilter = SubscriptionTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
|
||||
|
||||
// 1. 查询订阅
|
||||
var subscription = await subscriptionRepository.FindByIdAsync(
|
||||
request.SubscriptionId,
|
||||
cancellationToken,
|
||||
ignoreTenantFilter: ignoreTenantFilter);
|
||||
cancellationToken);
|
||||
|
||||
if (subscription == null)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Dto;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Queries;
|
||||
using TakeoutSaaS.Application.App.Tenants;
|
||||
@@ -12,20 +11,17 @@ namespace TakeoutSaaS.Application.App.Subscriptions.Handlers;
|
||||
/// 订阅详情查询处理器。
|
||||
/// </summary>
|
||||
public sealed class GetSubscriptionDetailQueryHandler(
|
||||
ISubscriptionRepository subscriptionRepository,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
ISubscriptionRepository subscriptionRepository)
|
||||
: IRequestHandler<GetSubscriptionDetailQuery, SubscriptionDetailDto?>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<SubscriptionDetailDto?> Handle(GetSubscriptionDetailQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var ignoreTenantFilter = SubscriptionTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
|
||||
|
||||
// 1. 查询订阅基础信息
|
||||
var detail = await subscriptionRepository.GetDetailAsync(
|
||||
request.SubscriptionId,
|
||||
cancellationToken,
|
||||
ignoreTenantFilter: ignoreTenantFilter);
|
||||
includeDeleted: request.IncludeDeleted);
|
||||
|
||||
if (detail == null)
|
||||
{
|
||||
@@ -36,7 +32,7 @@ public sealed class GetSubscriptionDetailQueryHandler(
|
||||
var quotaUsages = await subscriptionRepository.GetQuotaUsagesAsync(
|
||||
detail.Subscription.TenantId,
|
||||
cancellationToken,
|
||||
ignoreTenantFilter: ignoreTenantFilter);
|
||||
includeDeleted: request.IncludeDeleted);
|
||||
|
||||
var quotaUsageDtos = BuildQuotaUsageDtos(detail.Package, quotaUsages);
|
||||
|
||||
@@ -118,7 +114,7 @@ public sealed class GetSubscriptionDetailQueryHandler(
|
||||
});
|
||||
}
|
||||
|
||||
// Add any extra quota usages not covered by package fields (e.g. promotion slots).
|
||||
// 补充套餐字段未覆盖的配额使用项(例如营销槽位等扩展配额)
|
||||
foreach (var usage in usageByType.Values)
|
||||
{
|
||||
if (baselineTypes.Any(x => x.Type == usage.QuotaType))
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Dto;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Queries;
|
||||
using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
@@ -11,15 +10,12 @@ namespace TakeoutSaaS.Application.App.Subscriptions.Handlers;
|
||||
/// 订阅分页查询处理器。
|
||||
/// </summary>
|
||||
public sealed class GetSubscriptionListQueryHandler(
|
||||
ISubscriptionRepository subscriptionRepository,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
ISubscriptionRepository subscriptionRepository)
|
||||
: IRequestHandler<GetSubscriptionListQuery, PagedResult<SubscriptionListDto>>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<PagedResult<SubscriptionListDto>> Handle(GetSubscriptionListQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var ignoreTenantFilter = SubscriptionTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
|
||||
|
||||
// 1. 构建查询过滤条件
|
||||
var filter = new SubscriptionSearchFilter
|
||||
{
|
||||
@@ -37,7 +33,7 @@ public sealed class GetSubscriptionListQueryHandler(
|
||||
var (items, total) = await subscriptionRepository.SearchPagedAsync(
|
||||
filter,
|
||||
cancellationToken,
|
||||
ignoreTenantFilter: ignoreTenantFilter);
|
||||
includeDeleted: request.IncludeDeleted);
|
||||
|
||||
// 3. 映射为 DTO
|
||||
var dtos = items.Select(x => new SubscriptionListDto
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Text.Json;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Commands;
|
||||
@@ -14,7 +13,6 @@ namespace TakeoutSaaS.Application.App.Subscriptions.Handlers;
|
||||
/// </summary>
|
||||
public sealed class ProcessAutoRenewalCommandHandler(
|
||||
ISubscriptionRepository subscriptionRepository,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ITenantBillingRepository billingRepository,
|
||||
IIdGenerator idGenerator,
|
||||
ILogger<ProcessAutoRenewalCommandHandler> logger)
|
||||
@@ -23,8 +21,6 @@ public sealed class ProcessAutoRenewalCommandHandler(
|
||||
/// <inheritdoc />
|
||||
public async Task<ProcessAutoRenewalResult> Handle(ProcessAutoRenewalCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var ignoreTenantFilter = SubscriptionTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
|
||||
|
||||
// 1. 计算续费阈值时间
|
||||
var now = DateTime.UtcNow;
|
||||
var renewalThreshold = now.AddDays(request.RenewalDaysBeforeExpiry);
|
||||
@@ -33,8 +29,7 @@ public sealed class ProcessAutoRenewalCommandHandler(
|
||||
var candidates = await subscriptionRepository.FindAutoRenewalCandidatesAsync(
|
||||
now,
|
||||
renewalThreshold,
|
||||
cancellationToken,
|
||||
ignoreTenantFilter: ignoreTenantFilter);
|
||||
cancellationToken);
|
||||
var createdBillCount = 0;
|
||||
|
||||
// 3. 遍历候选订阅,生成账单
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Text.Json;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Commands;
|
||||
@@ -15,7 +14,6 @@ namespace TakeoutSaaS.Application.App.Subscriptions.Handlers;
|
||||
/// </summary>
|
||||
public sealed class ProcessRenewalRemindersCommandHandler(
|
||||
ISubscriptionRepository subscriptionRepository,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ITenantNotificationRepository notificationRepository,
|
||||
IIdGenerator idGenerator,
|
||||
ILogger<ProcessRenewalRemindersCommandHandler> logger)
|
||||
@@ -26,8 +24,6 @@ public sealed class ProcessRenewalRemindersCommandHandler(
|
||||
/// <inheritdoc />
|
||||
public async Task<ProcessRenewalRemindersResult> Handle(ProcessRenewalRemindersCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var ignoreTenantFilter = SubscriptionTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
|
||||
|
||||
// 1. 读取提醒配置
|
||||
var now = DateTime.UtcNow;
|
||||
var candidateCount = 0;
|
||||
@@ -46,8 +42,7 @@ public sealed class ProcessRenewalRemindersCommandHandler(
|
||||
var candidates = await subscriptionRepository.FindRenewalReminderCandidatesAsync(
|
||||
startOfDay,
|
||||
endOfDay,
|
||||
cancellationToken,
|
||||
ignoreTenantFilter: ignoreTenantFilter);
|
||||
cancellationToken);
|
||||
candidateCount += candidates.Count;
|
||||
|
||||
foreach (var item in candidates)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Commands;
|
||||
using TakeoutSaaS.Domain.Tenants.Enums;
|
||||
@@ -12,26 +11,21 @@ namespace TakeoutSaaS.Application.App.Subscriptions.Handlers;
|
||||
/// </summary>
|
||||
public sealed class ProcessSubscriptionExpiryCommandHandler(
|
||||
ISubscriptionRepository subscriptionRepository,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<ProcessSubscriptionExpiryCommandHandler> logger)
|
||||
: IRequestHandler<ProcessSubscriptionExpiryCommand, ProcessSubscriptionExpiryResult>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<ProcessSubscriptionExpiryResult> Handle(ProcessSubscriptionExpiryCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var ignoreTenantFilter = SubscriptionTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
|
||||
|
||||
// 1. 查询到期订阅
|
||||
var now = DateTime.UtcNow;
|
||||
var expiredActive = await subscriptionRepository.FindExpiredActiveSubscriptionsAsync(
|
||||
now,
|
||||
cancellationToken,
|
||||
ignoreTenantFilter: ignoreTenantFilter);
|
||||
cancellationToken);
|
||||
var gracePeriodExpired = await subscriptionRepository.FindGracePeriodExpiredSubscriptionsAsync(
|
||||
now,
|
||||
request.GracePeriodDays,
|
||||
cancellationToken,
|
||||
ignoreTenantFilter: ignoreTenantFilter);
|
||||
cancellationToken);
|
||||
|
||||
// 2. 更新订阅状态
|
||||
foreach (var subscription in expiredActive)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Commands;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Dto;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Queries;
|
||||
@@ -12,20 +11,16 @@ namespace TakeoutSaaS.Application.App.Subscriptions.Handlers;
|
||||
/// </summary>
|
||||
public sealed class UpdateSubscriptionCommandHandler(
|
||||
ISubscriptionRepository subscriptionRepository,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IMediator mediator)
|
||||
: IRequestHandler<UpdateSubscriptionCommand, SubscriptionDetailDto?>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<SubscriptionDetailDto?> Handle(UpdateSubscriptionCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var ignoreTenantFilter = SubscriptionTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
|
||||
|
||||
// 1. 查询订阅
|
||||
var subscription = await subscriptionRepository.FindByIdAsync(
|
||||
request.SubscriptionId,
|
||||
cancellationToken,
|
||||
ignoreTenantFilter: ignoreTenantFilter);
|
||||
cancellationToken);
|
||||
|
||||
if (subscription == null)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Commands;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Dto;
|
||||
using TakeoutSaaS.Application.App.Subscriptions.Queries;
|
||||
@@ -12,20 +11,16 @@ namespace TakeoutSaaS.Application.App.Subscriptions.Handlers;
|
||||
/// </summary>
|
||||
public sealed class UpdateSubscriptionStatusCommandHandler(
|
||||
ISubscriptionRepository subscriptionRepository,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IMediator mediator)
|
||||
: IRequestHandler<UpdateSubscriptionStatusCommand, SubscriptionDetailDto?>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<SubscriptionDetailDto?> Handle(UpdateSubscriptionStatusCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var ignoreTenantFilter = SubscriptionTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
|
||||
|
||||
// 1. 查询订阅
|
||||
var subscription = await subscriptionRepository.FindByIdAsync(
|
||||
request.SubscriptionId,
|
||||
cancellationToken,
|
||||
ignoreTenantFilter: ignoreTenantFilter);
|
||||
cancellationToken);
|
||||
|
||||
if (subscription == null)
|
||||
{
|
||||
|
||||
@@ -12,4 +12,9 @@ public sealed record GetSubscriptionDetailQuery : IRequest<SubscriptionDetailDto
|
||||
/// 订阅 ID。
|
||||
/// </summary>
|
||||
public long SubscriptionId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含已软删除数据。
|
||||
/// </summary>
|
||||
public bool IncludeDeleted { get; init; }
|
||||
}
|
||||
|
||||
@@ -40,6 +40,11 @@ public sealed record GetSubscriptionListQuery : IRequest<PagedResult<Subscriptio
|
||||
/// </summary>
|
||||
public bool? AutoRenew { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含已软删除数据。
|
||||
/// </summary>
|
||||
public bool IncludeDeleted { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 页码(从 1 开始)。
|
||||
/// </summary>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace TakeoutSaaS.Application.App.Subscriptions;
|
||||
|
||||
internal static class SubscriptionTenantAccess
|
||||
{
|
||||
private const string PermissionClaimType = "permission";
|
||||
private const string PlatformAdminRole = "PlatformAdmin";
|
||||
|
||||
public static bool ShouldIgnoreTenantFilter(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
var httpContext = httpContextAccessor.HttpContext;
|
||||
if (httpContext == null)
|
||||
{
|
||||
// Background jobs / out-of-request execution should process across tenants.
|
||||
return true;
|
||||
}
|
||||
|
||||
var user = httpContext.User;
|
||||
if (user?.Identity?.IsAuthenticated != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (user.IsInRole(PlatformAdminRole))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var permissions = user.FindAll(PermissionClaimType)
|
||||
.Select(c => c.Value?.Trim())
|
||||
.Where(v => !string.IsNullOrWhiteSpace(v))
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// Platform-level tenant permissions imply cross-tenant visibility.
|
||||
return permissions.Contains("tenant:read");
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using TakeoutSaaS.Application.App.Tenants.Commands;
|
||||
using TakeoutSaaS.Application.App.Tenants.Dto;
|
||||
using TakeoutSaaS.Application.Identity.Commands;
|
||||
using TakeoutSaaS.Domain.Identity.Entities;
|
||||
using TakeoutSaaS.Domain.Identity.Enums;
|
||||
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||
using TakeoutSaaS.Domain.Tenants.Entities;
|
||||
using TakeoutSaaS.Domain.Tenants.Enums;
|
||||
@@ -215,6 +216,7 @@ public sealed class CreateTenantManuallyCommandHandler(
|
||||
// 13. 创建租户管理员账号
|
||||
var adminUser = new IdentityUser
|
||||
{
|
||||
Portal = PortalType.Tenant,
|
||||
TenantId = tenant.Id,
|
||||
Account = normalizedAccount,
|
||||
DisplayName = request.AdminDisplayName.Trim(),
|
||||
@@ -234,7 +236,7 @@ public sealed class CreateTenantManuallyCommandHandler(
|
||||
TemplateCodes = new[] { "tenant-admin" }
|
||||
}, cancellationToken);
|
||||
|
||||
var tenantAdminRole = await roleRepository.FindByCodeAsync("tenant-admin", tenant.Id, cancellationToken);
|
||||
var tenantAdminRole = await roleRepository.FindByCodeAsync(PortalType.Tenant, tenant.Id, "tenant-admin", cancellationToken);
|
||||
if (tenantAdminRole != null)
|
||||
{
|
||||
await mediator.Send(new AssignUserRolesCommand
|
||||
|
||||
@@ -4,6 +4,7 @@ using TakeoutSaaS.Application.App.Tenants.Commands;
|
||||
using TakeoutSaaS.Application.App.Tenants.Dto;
|
||||
using TakeoutSaaS.Application.Identity.Commands;
|
||||
using TakeoutSaaS.Domain.Identity.Entities;
|
||||
using TakeoutSaaS.Domain.Identity.Enums;
|
||||
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||
using TakeoutSaaS.Domain.Tenants.Entities;
|
||||
using TakeoutSaaS.Domain.Tenants.Enums;
|
||||
@@ -86,6 +87,7 @@ public sealed class SelfRegisterTenantCommandHandler(
|
||||
// 7. 使用用户自设密码创建管理员
|
||||
var adminUser = new IdentityUser
|
||||
{
|
||||
Portal = PortalType.Tenant,
|
||||
TenantId = tenant.Id,
|
||||
Account = normalizedAccount,
|
||||
DisplayName = string.IsNullOrWhiteSpace(request.AdminDisplayName) ? normalizedAccount : request.AdminDisplayName!.Trim(),
|
||||
@@ -109,7 +111,7 @@ public sealed class SelfRegisterTenantCommandHandler(
|
||||
}, cancellationToken);
|
||||
|
||||
// 9. 绑定租户管理员角色
|
||||
var tenantAdminRole = await roleRepository.FindByCodeAsync("tenant-admin", tenant.Id, cancellationToken);
|
||||
var tenantAdminRole = await roleRepository.FindByCodeAsync(PortalType.Tenant, tenant.Id, "tenant-admin", cancellationToken);
|
||||
if (tenantAdminRole != null)
|
||||
{
|
||||
await mediator.Send(new AssignUserRolesCommand
|
||||
|
||||
Reference in New Issue
Block a user