chore: 提交现有修改
This commit is contained in:
@@ -16,42 +16,34 @@ namespace TakeoutSaaS.Infrastructure.App.Persistence;
|
||||
/// <summary>
|
||||
/// 业务数据种子,确保默认租户与基础字典可重复执行。
|
||||
/// </summary>
|
||||
public sealed class AppDataSeeder : IHostedService
|
||||
/// <remarks>
|
||||
/// 初始化种子服务。
|
||||
/// </remarks>
|
||||
public sealed class AppDataSeeder(
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger<AppDataSeeder> logger,
|
||||
IOptions<AppSeedOptions> options) : IHostedService
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ILogger<AppDataSeeder> _logger;
|
||||
private readonly AppSeedOptions _options;
|
||||
private readonly AppSeedOptions _options = options.Value;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化种子服务。
|
||||
/// </summary>
|
||||
public AppDataSeeder(
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger<AppDataSeeder> logger,
|
||||
IOptions<AppSeedOptions> options)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_logger = logger;
|
||||
_options = options.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (!_options.Enabled)
|
||||
{
|
||||
_logger.LogInformation("AppSeed 未启用,跳过业务数据初始化");
|
||||
logger.LogInformation("AppSeed 未启用,跳过业务数据初始化");
|
||||
return;
|
||||
}
|
||||
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
var appDbContext = scope.ServiceProvider.GetRequiredService<TakeoutAppDbContext>();
|
||||
var dictionaryDbContext = scope.ServiceProvider.GetRequiredService<DictionaryDbContext>();
|
||||
|
||||
var defaultTenantId = await EnsureDefaultTenantAsync(appDbContext, cancellationToken);
|
||||
await EnsureDictionarySeedsAsync(dictionaryDbContext, defaultTenantId, cancellationToken);
|
||||
|
||||
_logger.LogInformation("AppSeed 完成业务数据初始化");
|
||||
logger.LogInformation("AppSeed 完成业务数据初始化");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -65,7 +57,7 @@ public sealed class AppDataSeeder : IHostedService
|
||||
var tenantOptions = _options.DefaultTenant;
|
||||
if (tenantOptions == null || string.IsNullOrWhiteSpace(tenantOptions.Code) || string.IsNullOrWhiteSpace(tenantOptions.Name))
|
||||
{
|
||||
_logger.LogInformation("AppSeed 未配置默认租户,跳过租户种子");
|
||||
logger.LogInformation("AppSeed 未配置默认租户,跳过租户种子");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -89,7 +81,7 @@ public sealed class AppDataSeeder : IHostedService
|
||||
|
||||
await dbContext.Tenants.AddAsync(tenant, cancellationToken);
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
_logger.LogInformation("AppSeed 已创建默认租户 {TenantCode}", code);
|
||||
logger.LogInformation("AppSeed 已创建默认租户 {TenantCode}", code);
|
||||
return tenant.Id;
|
||||
}
|
||||
|
||||
@@ -129,11 +121,11 @@ public sealed class AppDataSeeder : IHostedService
|
||||
{
|
||||
dbContext.Tenants.Update(existingTenant);
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
_logger.LogInformation("AppSeed 已更新默认租户 {TenantCode}", code);
|
||||
logger.LogInformation("AppSeed 已更新默认租户 {TenantCode}", code);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("AppSeed 默认租户 {TenantCode} 已存在且无需更新", code);
|
||||
logger.LogInformation("AppSeed 默认租户 {TenantCode} 已存在且无需更新", code);
|
||||
}
|
||||
|
||||
return existingTenant.Id;
|
||||
@@ -149,7 +141,7 @@ public sealed class AppDataSeeder : IHostedService
|
||||
|
||||
if (!hasDictionaryGroups)
|
||||
{
|
||||
_logger.LogInformation("AppSeed 未配置基础字典,跳过字典种子");
|
||||
logger.LogInformation("AppSeed 未配置基础字典,跳过字典种子");
|
||||
}
|
||||
|
||||
if (hasDictionaryGroups)
|
||||
@@ -158,7 +150,7 @@ public sealed class AppDataSeeder : IHostedService
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(groupOptions.Code) || string.IsNullOrWhiteSpace(groupOptions.Name))
|
||||
{
|
||||
_logger.LogWarning("AppSeed 跳过字典分组,Code 或 Name 为空");
|
||||
logger.LogWarning("AppSeed 跳过字典分组,Code 或 Name 为空");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -183,7 +175,7 @@ public sealed class AppDataSeeder : IHostedService
|
||||
};
|
||||
|
||||
await dbContext.DictionaryGroups.AddAsync(group, cancellationToken);
|
||||
_logger.LogInformation("AppSeed 创建字典分组 {GroupCode} (Tenant: {TenantId})", code, tenantId);
|
||||
logger.LogInformation("AppSeed 创建字典分组 {GroupCode} (Tenant: {TenantId})", code, tenantId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -236,7 +228,7 @@ public sealed class AppDataSeeder : IHostedService
|
||||
|
||||
if (systemParameters.Count == 0)
|
||||
{
|
||||
_logger.LogInformation("AppSeed 未配置系统参数,跳过系统参数种子");
|
||||
logger.LogInformation("AppSeed 未配置系统参数,跳过系统参数种子");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -246,7 +238,7 @@ public sealed class AppDataSeeder : IHostedService
|
||||
|
||||
if (!grouped.Any())
|
||||
{
|
||||
_logger.LogInformation("AppSeed 系统参数配置为空,跳过系统参数种子");
|
||||
logger.LogInformation("AppSeed 系统参数配置为空,跳过系统参数种子");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -271,7 +263,7 @@ public sealed class AppDataSeeder : IHostedService
|
||||
};
|
||||
|
||||
await dbContext.DictionaryGroups.AddAsync(dictionaryGroup, cancellationToken);
|
||||
_logger.LogInformation("AppSeed 创建系统参数分组 (Tenant: {TenantId})", tenantId);
|
||||
logger.LogInformation("AppSeed 创建系统参数分组 (Tenant: {TenantId})", tenantId);
|
||||
}
|
||||
|
||||
var seedItems = group.Select(x => new DictionarySeedItemOptions
|
||||
|
||||
@@ -10,22 +10,17 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories;
|
||||
/// <summary>
|
||||
/// 配送聚合的 EF Core 仓储实现。
|
||||
/// </summary>
|
||||
public sealed class EfDeliveryRepository : IDeliveryRepository
|
||||
/// <remarks>
|
||||
/// 初始化仓储。
|
||||
/// </remarks>
|
||||
public sealed class EfDeliveryRepository(TakeoutAppDbContext context) : IDeliveryRepository
|
||||
{
|
||||
private readonly TakeoutAppDbContext _context;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化仓储。
|
||||
/// </summary>
|
||||
public EfDeliveryRepository(TakeoutAppDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<DeliveryOrder?> FindByIdAsync(long deliveryOrderId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.DeliveryOrders
|
||||
return context.DeliveryOrders
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.Id == deliveryOrderId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
@@ -34,7 +29,7 @@ public sealed class EfDeliveryRepository : IDeliveryRepository
|
||||
/// <inheritdoc />
|
||||
public Task<DeliveryOrder?> FindByOrderIdAsync(long orderId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.DeliveryOrders
|
||||
return context.DeliveryOrders
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.OrderId == orderId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
@@ -43,7 +38,7 @@ public sealed class EfDeliveryRepository : IDeliveryRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<DeliveryEvent>> GetEventsAsync(long deliveryOrderId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var events = await _context.DeliveryEvents
|
||||
var events = await context.DeliveryEvents
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.DeliveryOrderId == deliveryOrderId)
|
||||
.OrderBy(x => x.CreatedAt)
|
||||
@@ -55,25 +50,25 @@ public sealed class EfDeliveryRepository : IDeliveryRepository
|
||||
/// <inheritdoc />
|
||||
public Task AddDeliveryOrderAsync(DeliveryOrder deliveryOrder, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.DeliveryOrders.AddAsync(deliveryOrder, cancellationToken).AsTask();
|
||||
return context.DeliveryOrders.AddAsync(deliveryOrder, cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddEventAsync(DeliveryEvent deliveryEvent, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.DeliveryEvents.AddAsync(deliveryEvent, cancellationToken).AsTask();
|
||||
return context.DeliveryEvents.AddAsync(deliveryEvent, cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.SaveChangesAsync(cancellationToken);
|
||||
return context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<DeliveryOrder>> SearchAsync(long tenantId, DeliveryStatus? status, long? orderId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = _context.DeliveryOrders
|
||||
var query = context.DeliveryOrders
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId);
|
||||
|
||||
@@ -95,23 +90,23 @@ public sealed class EfDeliveryRepository : IDeliveryRepository
|
||||
/// <inheritdoc />
|
||||
public Task UpdateDeliveryOrderAsync(DeliveryOrder deliveryOrder, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.DeliveryOrders.Update(deliveryOrder);
|
||||
context.DeliveryOrders.Update(deliveryOrder);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DeleteDeliveryOrderAsync(long deliveryOrderId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var events = await _context.DeliveryEvents
|
||||
var events = await context.DeliveryEvents
|
||||
.Where(x => x.TenantId == tenantId && x.DeliveryOrderId == deliveryOrderId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (events.Count > 0)
|
||||
{
|
||||
_context.DeliveryEvents.RemoveRange(events);
|
||||
context.DeliveryEvents.RemoveRange(events);
|
||||
}
|
||||
|
||||
var existing = await _context.DeliveryOrders
|
||||
var existing = await context.DeliveryOrders
|
||||
.Where(x => x.TenantId == tenantId && x.Id == deliveryOrderId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
@@ -120,6 +115,6 @@ public sealed class EfDeliveryRepository : IDeliveryRepository
|
||||
return;
|
||||
}
|
||||
|
||||
_context.DeliveryOrders.Remove(existing);
|
||||
context.DeliveryOrders.Remove(existing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,22 +10,17 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories;
|
||||
/// <summary>
|
||||
/// 商户聚合的 EF Core 仓储实现。
|
||||
/// </summary>
|
||||
public sealed class EfMerchantRepository : IMerchantRepository
|
||||
/// <remarks>
|
||||
/// 初始化仓储。
|
||||
/// </remarks>
|
||||
public sealed class EfMerchantRepository(TakeoutAppDbContext context) : IMerchantRepository
|
||||
{
|
||||
private readonly TakeoutAppDbContext _context;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化仓储。
|
||||
/// </summary>
|
||||
public EfMerchantRepository(TakeoutAppDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Merchant?> FindByIdAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.Merchants
|
||||
return context.Merchants
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.Id == merchantId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
@@ -34,7 +29,7 @@ public sealed class EfMerchantRepository : IMerchantRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<Merchant>> SearchAsync(long tenantId, MerchantStatus? status, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = _context.Merchants
|
||||
var query = context.Merchants
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId);
|
||||
|
||||
@@ -51,7 +46,7 @@ public sealed class EfMerchantRepository : IMerchantRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<MerchantStaff>> GetStaffAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var staffs = await _context.MerchantStaff
|
||||
var staffs = await context.MerchantStaff
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.MerchantId == merchantId)
|
||||
.OrderBy(x => x.Name)
|
||||
@@ -63,7 +58,7 @@ public sealed class EfMerchantRepository : IMerchantRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<MerchantContract>> GetContractsAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var contracts = await _context.MerchantContracts
|
||||
var contracts = await context.MerchantContracts
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.MerchantId == merchantId)
|
||||
.OrderByDescending(x => x.CreatedAt)
|
||||
@@ -75,7 +70,7 @@ public sealed class EfMerchantRepository : IMerchantRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<MerchantDocument>> GetDocumentsAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var documents = await _context.MerchantDocuments
|
||||
var documents = await context.MerchantDocuments
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.MerchantId == merchantId)
|
||||
.OrderBy(x => x.CreatedAt)
|
||||
@@ -87,44 +82,44 @@ public sealed class EfMerchantRepository : IMerchantRepository
|
||||
/// <inheritdoc />
|
||||
public Task AddMerchantAsync(Merchant merchant, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.Merchants.AddAsync(merchant, cancellationToken).AsTask();
|
||||
return context.Merchants.AddAsync(merchant, cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddStaffAsync(MerchantStaff staff, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.MerchantStaff.AddAsync(staff, cancellationToken).AsTask();
|
||||
return context.MerchantStaff.AddAsync(staff, cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddContractAsync(MerchantContract contract, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.MerchantContracts.AddAsync(contract, cancellationToken).AsTask();
|
||||
return context.MerchantContracts.AddAsync(contract, cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddDocumentAsync(MerchantDocument document, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.MerchantDocuments.AddAsync(document, cancellationToken).AsTask();
|
||||
return context.MerchantDocuments.AddAsync(document, cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.SaveChangesAsync(cancellationToken);
|
||||
return context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UpdateMerchantAsync(Merchant merchant, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.Merchants.Update(merchant);
|
||||
context.Merchants.Update(merchant);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DeleteMerchantAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var existing = await _context.Merchants
|
||||
var existing = await context.Merchants
|
||||
.Where(x => x.TenantId == tenantId && x.Id == merchantId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
@@ -133,6 +128,6 @@ public sealed class EfMerchantRepository : IMerchantRepository
|
||||
return;
|
||||
}
|
||||
|
||||
_context.Merchants.Remove(existing);
|
||||
context.Merchants.Remove(existing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,22 +11,17 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories;
|
||||
/// <summary>
|
||||
/// 订单聚合的 EF Core 仓储实现。
|
||||
/// </summary>
|
||||
public sealed class EfOrderRepository : IOrderRepository
|
||||
/// <remarks>
|
||||
/// 初始化仓储。
|
||||
/// </remarks>
|
||||
public sealed class EfOrderRepository(TakeoutAppDbContext context) : IOrderRepository
|
||||
{
|
||||
private readonly TakeoutAppDbContext _context;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化仓储。
|
||||
/// </summary>
|
||||
public EfOrderRepository(TakeoutAppDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Order?> FindByIdAsync(long orderId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.Orders
|
||||
return context.Orders
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.Id == orderId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
@@ -35,7 +30,7 @@ public sealed class EfOrderRepository : IOrderRepository
|
||||
/// <inheritdoc />
|
||||
public Task<Order?> FindByOrderNoAsync(string orderNo, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.Orders
|
||||
return context.Orders
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.OrderNo == orderNo)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
@@ -44,7 +39,7 @@ public sealed class EfOrderRepository : IOrderRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<Order>> SearchAsync(long tenantId, OrderStatus? status, PaymentStatus? paymentStatus, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = _context.Orders
|
||||
var query = context.Orders
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId);
|
||||
|
||||
@@ -68,7 +63,7 @@ public sealed class EfOrderRepository : IOrderRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<OrderItem>> GetItemsAsync(long orderId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var items = await _context.OrderItems
|
||||
var items = await context.OrderItems
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.OrderId == orderId)
|
||||
.OrderBy(x => x.Id)
|
||||
@@ -80,7 +75,7 @@ public sealed class EfOrderRepository : IOrderRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<OrderStatusHistory>> GetStatusHistoryAsync(long orderId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var histories = await _context.OrderStatusHistories
|
||||
var histories = await context.OrderStatusHistories
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.OrderId == orderId)
|
||||
.OrderBy(x => x.CreatedAt)
|
||||
@@ -92,7 +87,7 @@ public sealed class EfOrderRepository : IOrderRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<RefundRequest>> GetRefundsAsync(long orderId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var refunds = await _context.RefundRequests
|
||||
var refunds = await context.RefundRequests
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.OrderId == orderId)
|
||||
.OrderByDescending(x => x.CreatedAt)
|
||||
@@ -104,68 +99,68 @@ public sealed class EfOrderRepository : IOrderRepository
|
||||
/// <inheritdoc />
|
||||
public Task AddOrderAsync(Order order, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.Orders.AddAsync(order, cancellationToken).AsTask();
|
||||
return context.Orders.AddAsync(order, cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddItemsAsync(IEnumerable<OrderItem> items, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.OrderItems.AddRangeAsync(items, cancellationToken);
|
||||
return context.OrderItems.AddRangeAsync(items, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddStatusHistoryAsync(OrderStatusHistory history, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.OrderStatusHistories.AddAsync(history, cancellationToken).AsTask();
|
||||
return context.OrderStatusHistories.AddAsync(history, cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddRefundAsync(RefundRequest refund, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.RefundRequests.AddAsync(refund, cancellationToken).AsTask();
|
||||
return context.RefundRequests.AddAsync(refund, cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.SaveChangesAsync(cancellationToken);
|
||||
return context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UpdateOrderAsync(Order order, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.Orders.Update(order);
|
||||
context.Orders.Update(order);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DeleteOrderAsync(long orderId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var items = await _context.OrderItems
|
||||
var items = await context.OrderItems
|
||||
.Where(x => x.TenantId == tenantId && x.OrderId == orderId)
|
||||
.ToListAsync(cancellationToken);
|
||||
if (items.Count > 0)
|
||||
{
|
||||
_context.OrderItems.RemoveRange(items);
|
||||
context.OrderItems.RemoveRange(items);
|
||||
}
|
||||
|
||||
var histories = await _context.OrderStatusHistories
|
||||
var histories = await context.OrderStatusHistories
|
||||
.Where(x => x.TenantId == tenantId && x.OrderId == orderId)
|
||||
.ToListAsync(cancellationToken);
|
||||
if (histories.Count > 0)
|
||||
{
|
||||
_context.OrderStatusHistories.RemoveRange(histories);
|
||||
context.OrderStatusHistories.RemoveRange(histories);
|
||||
}
|
||||
|
||||
var refunds = await _context.RefundRequests
|
||||
var refunds = await context.RefundRequests
|
||||
.Where(x => x.TenantId == tenantId && x.OrderId == orderId)
|
||||
.ToListAsync(cancellationToken);
|
||||
if (refunds.Count > 0)
|
||||
{
|
||||
_context.RefundRequests.RemoveRange(refunds);
|
||||
context.RefundRequests.RemoveRange(refunds);
|
||||
}
|
||||
|
||||
var existing = await _context.Orders
|
||||
var existing = await context.Orders
|
||||
.Where(x => x.TenantId == tenantId && x.Id == orderId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (existing == null)
|
||||
@@ -173,6 +168,6 @@ public sealed class EfOrderRepository : IOrderRepository
|
||||
return;
|
||||
}
|
||||
|
||||
_context.Orders.Remove(existing);
|
||||
context.Orders.Remove(existing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,22 +10,17 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories;
|
||||
/// <summary>
|
||||
/// 支付记录的 EF Core 仓储实现。
|
||||
/// </summary>
|
||||
public sealed class EfPaymentRepository : IPaymentRepository
|
||||
/// <remarks>
|
||||
/// 初始化仓储。
|
||||
/// </remarks>
|
||||
public sealed class EfPaymentRepository(TakeoutAppDbContext context) : IPaymentRepository
|
||||
{
|
||||
private readonly TakeoutAppDbContext _context;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化仓储。
|
||||
/// </summary>
|
||||
public EfPaymentRepository(TakeoutAppDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<PaymentRecord?> FindByIdAsync(long paymentId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.PaymentRecords
|
||||
return context.PaymentRecords
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.Id == paymentId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
@@ -34,7 +29,7 @@ public sealed class EfPaymentRepository : IPaymentRepository
|
||||
/// <inheritdoc />
|
||||
public Task<PaymentRecord?> FindByOrderIdAsync(long orderId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.PaymentRecords
|
||||
return context.PaymentRecords
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.OrderId == orderId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
@@ -43,7 +38,7 @@ public sealed class EfPaymentRepository : IPaymentRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<PaymentRefundRecord>> GetRefundsAsync(long paymentId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var refunds = await _context.PaymentRefundRecords
|
||||
var refunds = await context.PaymentRefundRecords
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.PaymentRecordId == paymentId)
|
||||
.OrderByDescending(x => x.CreatedAt)
|
||||
@@ -55,19 +50,19 @@ public sealed class EfPaymentRepository : IPaymentRepository
|
||||
/// <inheritdoc />
|
||||
public Task AddPaymentAsync(PaymentRecord payment, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.PaymentRecords.AddAsync(payment, cancellationToken).AsTask();
|
||||
return context.PaymentRecords.AddAsync(payment, cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddRefundAsync(PaymentRefundRecord refund, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.PaymentRefundRecords.AddAsync(refund, cancellationToken).AsTask();
|
||||
return context.PaymentRefundRecords.AddAsync(refund, cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<PaymentRecord>> SearchAsync(long tenantId, PaymentStatus? status, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = _context.PaymentRecords
|
||||
var query = context.PaymentRecords
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId);
|
||||
|
||||
@@ -84,28 +79,28 @@ public sealed class EfPaymentRepository : IPaymentRepository
|
||||
/// <inheritdoc />
|
||||
public Task SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.SaveChangesAsync(cancellationToken);
|
||||
return context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UpdatePaymentAsync(PaymentRecord payment, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.PaymentRecords.Update(payment);
|
||||
context.PaymentRecords.Update(payment);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DeletePaymentAsync(long paymentId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var refunds = await _context.PaymentRefundRecords
|
||||
var refunds = await context.PaymentRefundRecords
|
||||
.Where(x => x.TenantId == tenantId && x.PaymentRecordId == paymentId)
|
||||
.ToListAsync(cancellationToken);
|
||||
if (refunds.Count > 0)
|
||||
{
|
||||
_context.PaymentRefundRecords.RemoveRange(refunds);
|
||||
context.PaymentRefundRecords.RemoveRange(refunds);
|
||||
}
|
||||
|
||||
var existing = await _context.PaymentRecords
|
||||
var existing = await context.PaymentRecords
|
||||
.Where(x => x.TenantId == tenantId && x.Id == paymentId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (existing == null)
|
||||
@@ -113,6 +108,6 @@ public sealed class EfPaymentRepository : IPaymentRepository
|
||||
return;
|
||||
}
|
||||
|
||||
_context.PaymentRecords.Remove(existing);
|
||||
context.PaymentRecords.Remove(existing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,22 +10,17 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories;
|
||||
/// <summary>
|
||||
/// 商品聚合的 EF Core 仓储实现。
|
||||
/// </summary>
|
||||
public sealed class EfProductRepository : IProductRepository
|
||||
/// <remarks>
|
||||
/// 初始化仓储。
|
||||
/// </remarks>
|
||||
public sealed class EfProductRepository(TakeoutAppDbContext context) : IProductRepository
|
||||
{
|
||||
private readonly TakeoutAppDbContext _context;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化仓储。
|
||||
/// </summary>
|
||||
public EfProductRepository(TakeoutAppDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Product?> FindByIdAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.Products
|
||||
return context.Products
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.Id == productId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
@@ -34,7 +29,7 @@ public sealed class EfProductRepository : IProductRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<Product>> SearchAsync(long tenantId, long? categoryId, ProductStatus? status, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = _context.Products
|
||||
var query = context.Products
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId);
|
||||
|
||||
@@ -58,7 +53,7 @@ public sealed class EfProductRepository : IProductRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<ProductCategory>> GetCategoriesAsync(long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var categories = await _context.ProductCategories
|
||||
var categories = await context.ProductCategories
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId)
|
||||
.OrderBy(x => x.SortOrder)
|
||||
@@ -70,7 +65,7 @@ public sealed class EfProductRepository : IProductRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<ProductSku>> GetSkusAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var skus = await _context.ProductSkus
|
||||
var skus = await context.ProductSkus
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.ProductId == productId)
|
||||
.OrderBy(x => x.SortOrder)
|
||||
@@ -82,7 +77,7 @@ public sealed class EfProductRepository : IProductRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<ProductAddonGroup>> GetAddonGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var groups = await _context.ProductAddonGroups
|
||||
var groups = await context.ProductAddonGroups
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.ProductId == productId)
|
||||
.OrderBy(x => x.SortOrder)
|
||||
@@ -94,7 +89,7 @@ public sealed class EfProductRepository : IProductRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<ProductAddonOption>> GetAddonOptionsAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var groupIds = await _context.ProductAddonGroups
|
||||
var groupIds = await context.ProductAddonGroups
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.ProductId == productId)
|
||||
.Select(x => x.Id)
|
||||
@@ -105,7 +100,7 @@ public sealed class EfProductRepository : IProductRepository
|
||||
return Array.Empty<ProductAddonOption>();
|
||||
}
|
||||
|
||||
var options = await _context.ProductAddonOptions
|
||||
var options = await context.ProductAddonOptions
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && groupIds.Contains(x.AddonGroupId))
|
||||
.OrderBy(x => x.SortOrder)
|
||||
@@ -117,7 +112,7 @@ public sealed class EfProductRepository : IProductRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<ProductAttributeGroup>> GetAttributeGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var groups = await _context.ProductAttributeGroups
|
||||
var groups = await context.ProductAttributeGroups
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.ProductId == productId)
|
||||
.OrderBy(x => x.SortOrder)
|
||||
@@ -129,7 +124,7 @@ public sealed class EfProductRepository : IProductRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<ProductAttributeOption>> GetAttributeOptionsAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var groupIds = await _context.ProductAttributeGroups
|
||||
var groupIds = await context.ProductAttributeGroups
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.ProductId == productId)
|
||||
.Select(x => x.Id)
|
||||
@@ -140,7 +135,7 @@ public sealed class EfProductRepository : IProductRepository
|
||||
return Array.Empty<ProductAttributeOption>();
|
||||
}
|
||||
|
||||
var options = await _context.ProductAttributeOptions
|
||||
var options = await context.ProductAttributeOptions
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && groupIds.Contains(x.AttributeGroupId))
|
||||
.OrderBy(x => x.SortOrder)
|
||||
@@ -152,7 +147,7 @@ public sealed class EfProductRepository : IProductRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<ProductMediaAsset>> GetMediaAssetsAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var assets = await _context.ProductMediaAssets
|
||||
var assets = await context.ProductMediaAssets
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.ProductId == productId)
|
||||
.OrderBy(x => x.SortOrder)
|
||||
@@ -164,7 +159,7 @@ public sealed class EfProductRepository : IProductRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<ProductPricingRule>> GetPricingRulesAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var rules = await _context.ProductPricingRules
|
||||
var rules = await context.ProductPricingRules
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.ProductId == productId)
|
||||
.OrderBy(x => x.SortOrder)
|
||||
@@ -176,59 +171,59 @@ public sealed class EfProductRepository : IProductRepository
|
||||
/// <inheritdoc />
|
||||
public Task AddCategoryAsync(ProductCategory category, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.ProductCategories.AddAsync(category, cancellationToken).AsTask();
|
||||
return context.ProductCategories.AddAsync(category, cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddProductAsync(Product product, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.Products.AddAsync(product, cancellationToken).AsTask();
|
||||
return context.Products.AddAsync(product, cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddSkusAsync(IEnumerable<ProductSku> skus, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.ProductSkus.AddRangeAsync(skus, cancellationToken);
|
||||
return context.ProductSkus.AddRangeAsync(skus, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddAddonGroupsAsync(IEnumerable<ProductAddonGroup> groups, IEnumerable<ProductAddonOption> options, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var addGroupsTask = _context.ProductAddonGroups.AddRangeAsync(groups, cancellationToken);
|
||||
var addOptionsTask = _context.ProductAddonOptions.AddRangeAsync(options, cancellationToken);
|
||||
var addGroupsTask = context.ProductAddonGroups.AddRangeAsync(groups, cancellationToken);
|
||||
var addOptionsTask = context.ProductAddonOptions.AddRangeAsync(options, cancellationToken);
|
||||
return Task.WhenAll(addGroupsTask, addOptionsTask);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddAttributeGroupsAsync(IEnumerable<ProductAttributeGroup> groups, IEnumerable<ProductAttributeOption> options, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var addGroupsTask = _context.ProductAttributeGroups.AddRangeAsync(groups, cancellationToken);
|
||||
var addOptionsTask = _context.ProductAttributeOptions.AddRangeAsync(options, cancellationToken);
|
||||
var addGroupsTask = context.ProductAttributeGroups.AddRangeAsync(groups, cancellationToken);
|
||||
var addOptionsTask = context.ProductAttributeOptions.AddRangeAsync(options, cancellationToken);
|
||||
return Task.WhenAll(addGroupsTask, addOptionsTask);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddMediaAssetsAsync(IEnumerable<ProductMediaAsset> assets, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.ProductMediaAssets.AddRangeAsync(assets, cancellationToken);
|
||||
return context.ProductMediaAssets.AddRangeAsync(assets, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddPricingRulesAsync(IEnumerable<ProductPricingRule> rules, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.ProductPricingRules.AddRangeAsync(rules, cancellationToken);
|
||||
return context.ProductPricingRules.AddRangeAsync(rules, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.SaveChangesAsync(cancellationToken);
|
||||
return context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UpdateProductAsync(Product product, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.Products.Update(product);
|
||||
context.Products.Update(product);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -241,7 +236,7 @@ public sealed class EfProductRepository : IProductRepository
|
||||
await RemoveAddonGroupsAsync(productId, tenantId, cancellationToken);
|
||||
await RemoveSkusAsync(productId, tenantId, cancellationToken);
|
||||
|
||||
var existing = await _context.Products
|
||||
var existing = await context.Products
|
||||
.Where(x => x.TenantId == tenantId && x.Id == productId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
@@ -250,20 +245,20 @@ public sealed class EfProductRepository : IProductRepository
|
||||
return;
|
||||
}
|
||||
|
||||
_context.Products.Remove(existing);
|
||||
context.Products.Remove(existing);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UpdateCategoryAsync(ProductCategory category, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.ProductCategories.Update(category);
|
||||
context.ProductCategories.Update(category);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DeleteCategoryAsync(long categoryId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var existing = await _context.ProductCategories
|
||||
var existing = await context.ProductCategories
|
||||
.Where(x => x.TenantId == tenantId && x.Id == categoryId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
@@ -272,13 +267,13 @@ public sealed class EfProductRepository : IProductRepository
|
||||
return;
|
||||
}
|
||||
|
||||
_context.ProductCategories.Remove(existing);
|
||||
context.ProductCategories.Remove(existing);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task RemoveSkusAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var skus = await _context.ProductSkus
|
||||
var skus = await context.ProductSkus
|
||||
.Where(x => x.TenantId == tenantId && x.ProductId == productId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
@@ -287,13 +282,13 @@ public sealed class EfProductRepository : IProductRepository
|
||||
return;
|
||||
}
|
||||
|
||||
_context.ProductSkus.RemoveRange(skus);
|
||||
context.ProductSkus.RemoveRange(skus);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task RemoveAddonGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var groupIds = await _context.ProductAddonGroups
|
||||
var groupIds = await context.ProductAddonGroups
|
||||
.Where(x => x.TenantId == tenantId && x.ProductId == productId)
|
||||
.Select(x => x.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
@@ -303,29 +298,29 @@ public sealed class EfProductRepository : IProductRepository
|
||||
return;
|
||||
}
|
||||
|
||||
var options = await _context.ProductAddonOptions
|
||||
var options = await context.ProductAddonOptions
|
||||
.Where(x => x.TenantId == tenantId && groupIds.Contains(x.AddonGroupId))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (options.Count > 0)
|
||||
{
|
||||
_context.ProductAddonOptions.RemoveRange(options);
|
||||
context.ProductAddonOptions.RemoveRange(options);
|
||||
}
|
||||
|
||||
var groups = await _context.ProductAddonGroups
|
||||
var groups = await context.ProductAddonGroups
|
||||
.Where(x => groupIds.Contains(x.Id))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (groups.Count > 0)
|
||||
{
|
||||
_context.ProductAddonGroups.RemoveRange(groups);
|
||||
context.ProductAddonGroups.RemoveRange(groups);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task RemoveAttributeGroupsAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var groupIds = await _context.ProductAttributeGroups
|
||||
var groupIds = await context.ProductAttributeGroups
|
||||
.Where(x => x.TenantId == tenantId && x.ProductId == productId)
|
||||
.Select(x => x.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
@@ -335,29 +330,29 @@ public sealed class EfProductRepository : IProductRepository
|
||||
return;
|
||||
}
|
||||
|
||||
var options = await _context.ProductAttributeOptions
|
||||
var options = await context.ProductAttributeOptions
|
||||
.Where(x => x.TenantId == tenantId && groupIds.Contains(x.AttributeGroupId))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (options.Count > 0)
|
||||
{
|
||||
_context.ProductAttributeOptions.RemoveRange(options);
|
||||
context.ProductAttributeOptions.RemoveRange(options);
|
||||
}
|
||||
|
||||
var groups = await _context.ProductAttributeGroups
|
||||
var groups = await context.ProductAttributeGroups
|
||||
.Where(x => groupIds.Contains(x.Id))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (groups.Count > 0)
|
||||
{
|
||||
_context.ProductAttributeGroups.RemoveRange(groups);
|
||||
context.ProductAttributeGroups.RemoveRange(groups);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task RemoveMediaAssetsAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var assets = await _context.ProductMediaAssets
|
||||
var assets = await context.ProductMediaAssets
|
||||
.Where(x => x.TenantId == tenantId && x.ProductId == productId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
@@ -366,13 +361,13 @@ public sealed class EfProductRepository : IProductRepository
|
||||
return;
|
||||
}
|
||||
|
||||
_context.ProductMediaAssets.RemoveRange(assets);
|
||||
context.ProductMediaAssets.RemoveRange(assets);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task RemovePricingRulesAsync(long productId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var rules = await _context.ProductPricingRules
|
||||
var rules = await context.ProductPricingRules
|
||||
.Where(x => x.TenantId == tenantId && x.ProductId == productId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
@@ -381,6 +376,6 @@ public sealed class EfProductRepository : IProductRepository
|
||||
return;
|
||||
}
|
||||
|
||||
_context.ProductPricingRules.RemoveRange(rules);
|
||||
context.ProductPricingRules.RemoveRange(rules);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,22 +10,17 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories;
|
||||
/// <summary>
|
||||
/// 门店聚合的 EF Core 仓储实现。
|
||||
/// </summary>
|
||||
public sealed class EfStoreRepository : IStoreRepository
|
||||
/// <remarks>
|
||||
/// 初始化仓储。
|
||||
/// </remarks>
|
||||
public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepository
|
||||
{
|
||||
private readonly TakeoutAppDbContext _context;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化仓储。
|
||||
/// </summary>
|
||||
public EfStoreRepository(TakeoutAppDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Store?> FindByIdAsync(long storeId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.Stores
|
||||
return context.Stores
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.Id == storeId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
@@ -34,7 +29,7 @@ public sealed class EfStoreRepository : IStoreRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<Store>> SearchAsync(long tenantId, StoreStatus? status, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = _context.Stores
|
||||
var query = context.Stores
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId);
|
||||
|
||||
@@ -53,7 +48,7 @@ public sealed class EfStoreRepository : IStoreRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<StoreBusinessHour>> GetBusinessHoursAsync(long storeId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var hours = await _context.StoreBusinessHours
|
||||
var hours = await context.StoreBusinessHours
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.StoreId == storeId)
|
||||
.OrderBy(x => x.DayOfWeek)
|
||||
@@ -66,7 +61,7 @@ public sealed class EfStoreRepository : IStoreRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<StoreDeliveryZone>> GetDeliveryZonesAsync(long storeId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var zones = await _context.StoreDeliveryZones
|
||||
var zones = await context.StoreDeliveryZones
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.StoreId == storeId)
|
||||
.OrderBy(x => x.SortOrder)
|
||||
@@ -78,7 +73,7 @@ public sealed class EfStoreRepository : IStoreRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<StoreHoliday>> GetHolidaysAsync(long storeId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var holidays = await _context.StoreHolidays
|
||||
var holidays = await context.StoreHolidays
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.StoreId == storeId)
|
||||
.OrderBy(x => x.Date)
|
||||
@@ -90,7 +85,7 @@ public sealed class EfStoreRepository : IStoreRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<StoreTableArea>> GetTableAreasAsync(long storeId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var areas = await _context.StoreTableAreas
|
||||
var areas = await context.StoreTableAreas
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.StoreId == storeId)
|
||||
.OrderBy(x => x.SortOrder)
|
||||
@@ -102,7 +97,7 @@ public sealed class EfStoreRepository : IStoreRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<StoreTable>> GetTablesAsync(long storeId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var tables = await _context.StoreTables
|
||||
var tables = await context.StoreTables
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.StoreId == storeId)
|
||||
.OrderBy(x => x.TableCode)
|
||||
@@ -114,7 +109,7 @@ public sealed class EfStoreRepository : IStoreRepository
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<StoreEmployeeShift>> GetShiftsAsync(long storeId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var shifts = await _context.StoreEmployeeShifts
|
||||
var shifts = await context.StoreEmployeeShifts
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.StoreId == storeId)
|
||||
.OrderBy(x => x.ShiftDate)
|
||||
@@ -127,62 +122,62 @@ public sealed class EfStoreRepository : IStoreRepository
|
||||
/// <inheritdoc />
|
||||
public Task AddStoreAsync(Store store, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.Stores.AddAsync(store, cancellationToken).AsTask();
|
||||
return context.Stores.AddAsync(store, cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddBusinessHoursAsync(IEnumerable<StoreBusinessHour> hours, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.StoreBusinessHours.AddRangeAsync(hours, cancellationToken);
|
||||
return context.StoreBusinessHours.AddRangeAsync(hours, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddDeliveryZonesAsync(IEnumerable<StoreDeliveryZone> zones, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.StoreDeliveryZones.AddRangeAsync(zones, cancellationToken);
|
||||
return context.StoreDeliveryZones.AddRangeAsync(zones, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddHolidaysAsync(IEnumerable<StoreHoliday> holidays, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.StoreHolidays.AddRangeAsync(holidays, cancellationToken);
|
||||
return context.StoreHolidays.AddRangeAsync(holidays, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddTableAreasAsync(IEnumerable<StoreTableArea> areas, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.StoreTableAreas.AddRangeAsync(areas, cancellationToken);
|
||||
return context.StoreTableAreas.AddRangeAsync(areas, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddTablesAsync(IEnumerable<StoreTable> tables, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.StoreTables.AddRangeAsync(tables, cancellationToken);
|
||||
return context.StoreTables.AddRangeAsync(tables, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddShiftsAsync(IEnumerable<StoreEmployeeShift> shifts, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.StoreEmployeeShifts.AddRangeAsync(shifts, cancellationToken);
|
||||
return context.StoreEmployeeShifts.AddRangeAsync(shifts, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.SaveChangesAsync(cancellationToken);
|
||||
return context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UpdateStoreAsync(Store store, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.Stores.Update(store);
|
||||
context.Stores.Update(store);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task DeleteStoreAsync(long storeId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var existing = await _context.Stores
|
||||
var existing = await context.Stores
|
||||
.Where(x => x.TenantId == tenantId && x.Id == storeId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
@@ -191,6 +186,6 @@ public sealed class EfStoreRepository : IStoreRepository
|
||||
return;
|
||||
}
|
||||
|
||||
_context.Stores.Remove(existing);
|
||||
context.Stores.Remove(existing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,24 +10,18 @@ namespace TakeoutSaaS.Infrastructure.Dictionary.Repositories;
|
||||
/// <summary>
|
||||
/// EF Core 字典仓储实现。
|
||||
/// </summary>
|
||||
public sealed class EfDictionaryRepository : IDictionaryRepository
|
||||
public sealed class EfDictionaryRepository(DictionaryDbContext context) : IDictionaryRepository
|
||||
{
|
||||
private readonly DictionaryDbContext _context;
|
||||
|
||||
public EfDictionaryRepository(DictionaryDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public Task<DictionaryGroup?> FindGroupByIdAsync(long id, CancellationToken cancellationToken = default)
|
||||
=> _context.DictionaryGroups.FirstOrDefaultAsync(group => group.Id == id, cancellationToken);
|
||||
=> context.DictionaryGroups.FirstOrDefaultAsync(group => group.Id == id, cancellationToken);
|
||||
|
||||
public Task<DictionaryGroup?> FindGroupByCodeAsync(string code, CancellationToken cancellationToken = default)
|
||||
=> _context.DictionaryGroups.FirstOrDefaultAsync(group => group.Code == code, cancellationToken);
|
||||
=> context.DictionaryGroups.FirstOrDefaultAsync(group => group.Code == code, cancellationToken);
|
||||
|
||||
public async Task<IReadOnlyList<DictionaryGroup>> SearchGroupsAsync(DictionaryScope? scope, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = _context.DictionaryGroups.AsNoTracking();
|
||||
var query = context.DictionaryGroups.AsNoTracking();
|
||||
if (scope.HasValue)
|
||||
{
|
||||
query = query.Where(group => group.Scope == scope.Value);
|
||||
@@ -40,22 +34,22 @@ public sealed class EfDictionaryRepository : IDictionaryRepository
|
||||
|
||||
public Task AddGroupAsync(DictionaryGroup group, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.DictionaryGroups.Add(group);
|
||||
context.DictionaryGroups.Add(group);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task RemoveGroupAsync(DictionaryGroup group, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.DictionaryGroups.Remove(group);
|
||||
context.DictionaryGroups.Remove(group);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<DictionaryItem?> FindItemByIdAsync(long id, CancellationToken cancellationToken = default)
|
||||
=> _context.DictionaryItems.FirstOrDefaultAsync(item => item.Id == id, cancellationToken);
|
||||
=> context.DictionaryItems.FirstOrDefaultAsync(item => item.Id == id, cancellationToken);
|
||||
|
||||
public async Task<IReadOnlyList<DictionaryItem>> GetItemsByGroupIdAsync(long groupId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _context.DictionaryItems
|
||||
return await context.DictionaryItems
|
||||
.AsNoTracking()
|
||||
.Where(item => item.GroupId == groupId)
|
||||
.OrderBy(item => item.SortOrder)
|
||||
@@ -64,18 +58,18 @@ public sealed class EfDictionaryRepository : IDictionaryRepository
|
||||
|
||||
public Task AddItemAsync(DictionaryItem item, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.DictionaryItems.Add(item);
|
||||
context.DictionaryItems.Add(item);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task RemoveItemAsync(DictionaryItem item, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.DictionaryItems.Remove(item);
|
||||
context.DictionaryItems.Remove(item);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
=> _context.SaveChangesAsync(cancellationToken);
|
||||
=> context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
public async Task<IReadOnlyList<DictionaryItem>> GetItemsByCodesAsync(IEnumerable<string> codes, long tenantId, bool includeSystem, CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -90,7 +84,7 @@ public sealed class EfDictionaryRepository : IDictionaryRepository
|
||||
return Array.Empty<DictionaryItem>();
|
||||
}
|
||||
|
||||
var query = _context.DictionaryItems
|
||||
var query = context.DictionaryItems
|
||||
.AsNoTracking()
|
||||
.IgnoreQueryFilters()
|
||||
.Include(item => item.Group)
|
||||
|
||||
@@ -10,22 +10,15 @@ namespace TakeoutSaaS.Infrastructure.Dictionary.Services;
|
||||
/// <summary>
|
||||
/// 基于 IDistributedCache 的字典缓存实现。
|
||||
/// </summary>
|
||||
public sealed class DistributedDictionaryCache : IDictionaryCache
|
||||
public sealed class DistributedDictionaryCache(IDistributedCache cache, IOptions<DictionaryCacheOptions> options) : IDictionaryCache
|
||||
{
|
||||
private readonly IDistributedCache _cache;
|
||||
private readonly DictionaryCacheOptions _options;
|
||||
private readonly DictionaryCacheOptions _options = options.Value;
|
||||
private readonly JsonSerializerOptions _serializerOptions = new(JsonSerializerDefaults.Web);
|
||||
|
||||
public DistributedDictionaryCache(IDistributedCache cache, IOptions<DictionaryCacheOptions> options)
|
||||
{
|
||||
_cache = cache;
|
||||
_options = options.Value;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<DictionaryItemDto>?> GetAsync(long tenantId, string code, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var cacheKey = BuildKey(tenantId, code);
|
||||
var payload = await _cache.GetAsync(cacheKey, cancellationToken);
|
||||
var payload = await cache.GetAsync(cacheKey, cancellationToken);
|
||||
if (payload == null || payload.Length == 0)
|
||||
{
|
||||
return null;
|
||||
@@ -42,13 +35,13 @@ public sealed class DistributedDictionaryCache : IDictionaryCache
|
||||
{
|
||||
SlidingExpiration = _options.SlidingExpiration
|
||||
};
|
||||
return _cache.SetAsync(cacheKey, payload, options, cancellationToken);
|
||||
return cache.SetAsync(cacheKey, payload, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task RemoveAsync(long tenantId, string code, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var cacheKey = BuildKey(tenantId, code);
|
||||
return _cache.RemoveAsync(cacheKey, cancellationToken);
|
||||
return cache.RemoveAsync(cacheKey, cancellationToken);
|
||||
}
|
||||
|
||||
private static string BuildKey(long tenantId, string code)
|
||||
|
||||
@@ -10,18 +10,12 @@ namespace TakeoutSaaS.Infrastructure.Identity.Persistence;
|
||||
/// <summary>
|
||||
/// EF Core 后台用户仓储实现。
|
||||
/// </summary>
|
||||
public sealed class EfIdentityUserRepository : IIdentityUserRepository
|
||||
public sealed class EfIdentityUserRepository(IdentityDbContext dbContext) : IIdentityUserRepository
|
||||
{
|
||||
private readonly IdentityDbContext _dbContext;
|
||||
|
||||
public EfIdentityUserRepository(IdentityDbContext dbContext)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
}
|
||||
|
||||
public Task<IdentityUser?> FindByAccountAsync(string account, CancellationToken cancellationToken = default)
|
||||
=> _dbContext.IdentityUsers.AsNoTracking().FirstOrDefaultAsync(x => x.Account == account, cancellationToken);
|
||||
=> dbContext.IdentityUsers.AsNoTracking().FirstOrDefaultAsync(x => x.Account == account, cancellationToken);
|
||||
|
||||
public Task<IdentityUser?> FindByIdAsync(long userId, CancellationToken cancellationToken = default)
|
||||
=> _dbContext.IdentityUsers.AsNoTracking().FirstOrDefaultAsync(x => x.Id == userId, cancellationToken);
|
||||
=> dbContext.IdentityUsers.AsNoTracking().FirstOrDefaultAsync(x => x.Id == userId, cancellationToken);
|
||||
}
|
||||
|
||||
@@ -10,24 +10,18 @@ namespace TakeoutSaaS.Infrastructure.Identity.Persistence;
|
||||
/// <summary>
|
||||
/// EF Core 小程序用户仓储实现。
|
||||
/// </summary>
|
||||
public sealed class EfMiniUserRepository : IMiniUserRepository
|
||||
public sealed class EfMiniUserRepository(IdentityDbContext dbContext) : IMiniUserRepository
|
||||
{
|
||||
private readonly IdentityDbContext _dbContext;
|
||||
|
||||
public EfMiniUserRepository(IdentityDbContext dbContext)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
}
|
||||
|
||||
public Task<MiniUser?> FindByOpenIdAsync(string openId, CancellationToken cancellationToken = default)
|
||||
=> _dbContext.MiniUsers.AsNoTracking().FirstOrDefaultAsync(x => x.OpenId == openId, cancellationToken);
|
||||
=> dbContext.MiniUsers.AsNoTracking().FirstOrDefaultAsync(x => x.OpenId == openId, cancellationToken);
|
||||
|
||||
public Task<MiniUser?> FindByIdAsync(long id, CancellationToken cancellationToken = default)
|
||||
=> _dbContext.MiniUsers.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id, cancellationToken);
|
||||
=> dbContext.MiniUsers.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id, cancellationToken);
|
||||
|
||||
public async Task<MiniUser> CreateOrUpdateAsync(string openId, string? unionId, string? nickname, string? avatar, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var user = await _dbContext.MiniUsers.FirstOrDefaultAsync(x => x.OpenId == openId, cancellationToken);
|
||||
var user = await dbContext.MiniUsers.FirstOrDefaultAsync(x => x.OpenId == openId, cancellationToken);
|
||||
if (user == null)
|
||||
{
|
||||
user = new MiniUser
|
||||
@@ -39,7 +33,7 @@ public sealed class EfMiniUserRepository : IMiniUserRepository
|
||||
Avatar = avatar,
|
||||
TenantId = tenantId
|
||||
};
|
||||
_dbContext.MiniUsers.Add(user);
|
||||
dbContext.MiniUsers.Add(user);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -48,7 +42,7 @@ public sealed class EfMiniUserRepository : IMiniUserRepository
|
||||
user.Avatar = avatar ?? user.Avatar;
|
||||
}
|
||||
|
||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,21 +13,14 @@ namespace TakeoutSaaS.Infrastructure.Identity.Services;
|
||||
/// <summary>
|
||||
/// Redis 登录限流实现。
|
||||
/// </summary>
|
||||
public sealed class RedisLoginRateLimiter : ILoginRateLimiter
|
||||
public sealed class RedisLoginRateLimiter(IDistributedCache cache, IOptions<LoginRateLimitOptions> options) : ILoginRateLimiter
|
||||
{
|
||||
private readonly IDistributedCache _cache;
|
||||
private readonly LoginRateLimitOptions _options;
|
||||
|
||||
public RedisLoginRateLimiter(IDistributedCache cache, IOptions<LoginRateLimitOptions> options)
|
||||
{
|
||||
_cache = cache;
|
||||
_options = options.Value;
|
||||
}
|
||||
private readonly LoginRateLimitOptions _options = options.Value;
|
||||
|
||||
public async Task EnsureAllowedAsync(string key, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var cacheKey = BuildKey(key);
|
||||
var current = await _cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
var current = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
var count = string.IsNullOrWhiteSpace(current) ? 0 : int.Parse(current);
|
||||
if (count >= _options.MaxAttempts)
|
||||
{
|
||||
@@ -35,7 +28,7 @@ public sealed class RedisLoginRateLimiter : ILoginRateLimiter
|
||||
}
|
||||
|
||||
count++;
|
||||
await _cache.SetStringAsync(
|
||||
await cache.SetStringAsync(
|
||||
cacheKey,
|
||||
count.ToString(),
|
||||
new DistributedCacheEntryOptions
|
||||
@@ -46,7 +39,7 @@ public sealed class RedisLoginRateLimiter : ILoginRateLimiter
|
||||
}
|
||||
|
||||
public Task ResetAsync(string key, CancellationToken cancellationToken = default)
|
||||
=> _cache.RemoveAsync(BuildKey(key), cancellationToken);
|
||||
=> cache.RemoveAsync(BuildKey(key), cancellationToken);
|
||||
|
||||
private static string BuildKey(string key) => $"identity:login:{key}";
|
||||
}
|
||||
|
||||
@@ -14,17 +14,10 @@ namespace TakeoutSaaS.Infrastructure.Identity.Services;
|
||||
/// <summary>
|
||||
/// Redis 刷新令牌存储。
|
||||
/// </summary>
|
||||
public sealed class RedisRefreshTokenStore : IRefreshTokenStore
|
||||
public sealed class RedisRefreshTokenStore(IDistributedCache cache, IOptions<RefreshTokenStoreOptions> options) : IRefreshTokenStore
|
||||
{
|
||||
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
|
||||
private readonly IDistributedCache _cache;
|
||||
private readonly RefreshTokenStoreOptions _options;
|
||||
|
||||
public RedisRefreshTokenStore(IDistributedCache cache, IOptions<RefreshTokenStoreOptions> options)
|
||||
{
|
||||
_cache = cache;
|
||||
_options = options.Value;
|
||||
}
|
||||
private readonly RefreshTokenStoreOptions _options = options.Value;
|
||||
|
||||
public async Task<RefreshTokenDescriptor> IssueAsync(long userId, DateTime expiresAt, CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -33,14 +26,14 @@ public sealed class RedisRefreshTokenStore : IRefreshTokenStore
|
||||
|
||||
var key = BuildKey(token);
|
||||
var entryOptions = new DistributedCacheEntryOptions { AbsoluteExpiration = expiresAt };
|
||||
await _cache.SetStringAsync(key, JsonSerializer.Serialize(descriptor, JsonOptions), entryOptions, cancellationToken);
|
||||
await cache.SetStringAsync(key, JsonSerializer.Serialize(descriptor, JsonOptions), entryOptions, cancellationToken);
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public async Task<RefreshTokenDescriptor?> GetAsync(string refreshToken, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var json = await _cache.GetStringAsync(BuildKey(refreshToken), cancellationToken);
|
||||
var json = await cache.GetStringAsync(BuildKey(refreshToken), cancellationToken);
|
||||
return string.IsNullOrWhiteSpace(json)
|
||||
? null
|
||||
: JsonSerializer.Deserialize<RefreshTokenDescriptor>(json, JsonOptions);
|
||||
@@ -56,7 +49,7 @@ public sealed class RedisRefreshTokenStore : IRefreshTokenStore
|
||||
|
||||
var updated = descriptor with { Revoked = true };
|
||||
var entryOptions = new DistributedCacheEntryOptions { AbsoluteExpiration = updated.ExpiresAt };
|
||||
await _cache.SetStringAsync(BuildKey(refreshToken), JsonSerializer.Serialize(updated, JsonOptions), entryOptions, cancellationToken);
|
||||
await cache.SetStringAsync(BuildKey(refreshToken), JsonSerializer.Serialize(updated, JsonOptions), entryOptions, cancellationToken);
|
||||
}
|
||||
|
||||
private string BuildKey(string token) => $"{_options.Prefix}{token}";
|
||||
|
||||
@@ -15,21 +15,14 @@ namespace TakeoutSaaS.Infrastructure.Identity.Services;
|
||||
/// <summary>
|
||||
/// 微信 code2Session 实现
|
||||
/// </summary>
|
||||
public sealed class WeChatAuthService : IWeChatAuthService
|
||||
public sealed class WeChatAuthService(HttpClient httpClient, IOptions<WeChatMiniOptions> options) : IWeChatAuthService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly WeChatMiniOptions _options;
|
||||
|
||||
public WeChatAuthService(HttpClient httpClient, IOptions<WeChatMiniOptions> options)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_options = options.Value;
|
||||
}
|
||||
private readonly WeChatMiniOptions _options = options.Value;
|
||||
|
||||
public async Task<WeChatSessionInfo> Code2SessionAsync(string code, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var requestUri = $"sns/jscode2session?appid={Uri.EscapeDataString(_options.AppId)}&secret={Uri.EscapeDataString(_options.Secret)}&js_code={Uri.EscapeDataString(code)}&grant_type=authorization_code";
|
||||
using var response = await _httpClient.GetAsync(requestUri, cancellationToken);
|
||||
using var response = await httpClient.GetAsync(requestUri, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var payload = await response.Content.ReadFromJsonAsync<WeChatSessionResponse>(cancellationToken: cancellationToken);
|
||||
|
||||
Reference in New Issue
Block a user