fix: 平台订阅管理支持跨租户查询

This commit is contained in:
2025-12-18 23:58:24 +08:00
parent 30f44b6009
commit 00eb357e6e
14 changed files with 307 additions and 57 deletions

View File

@@ -14,19 +14,31 @@ public sealed class EfSubscriptionRepository(TakeoutAppDbContext dbContext) : IS
#region
/// <inheritdoc />
public async Task<TenantSubscription?> FindByIdAsync(long subscriptionId, CancellationToken cancellationToken = default)
public async Task<TenantSubscription?> FindByIdAsync(
long subscriptionId,
CancellationToken cancellationToken = default,
bool ignoreTenantFilter = false)
{
return await dbContext.TenantSubscriptions
var query = ignoreTenantFilter
? dbContext.TenantSubscriptions.IgnoreQueryFilters()
: dbContext.TenantSubscriptions;
return await query
.FirstOrDefaultAsync(s => s.Id == subscriptionId, cancellationToken);
}
/// <inheritdoc />
public async Task<IReadOnlyList<TenantSubscription>> FindByIdsAsync(
IEnumerable<long> subscriptionIds,
CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default,
bool ignoreTenantFilter = false)
{
var ids = subscriptionIds.ToList();
return await dbContext.TenantSubscriptions
var query = ignoreTenantFilter
? dbContext.TenantSubscriptions.IgnoreQueryFilters()
: dbContext.TenantSubscriptions;
return await query
.Where(s => ids.Contains(s.Id))
.ToListAsync(cancellationToken);
}
@@ -34,10 +46,15 @@ public sealed class EfSubscriptionRepository(TakeoutAppDbContext dbContext) : IS
/// <inheritdoc />
public async Task<(IReadOnlyList<SubscriptionWithRelations> Items, int Total)> SearchPagedAsync(
SubscriptionSearchFilter filter,
CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default,
bool ignoreTenantFilter = false)
{
// 1. 构建基础查询
var query = dbContext.TenantSubscriptions
var subscriptionQuery = ignoreTenantFilter
? dbContext.TenantSubscriptions.IgnoreQueryFilters()
: dbContext.TenantSubscriptions;
var query = subscriptionQuery
.AsNoTracking()
.Join(
dbContext.Tenants,
@@ -113,9 +130,16 @@ public sealed class EfSubscriptionRepository(TakeoutAppDbContext dbContext) : IS
}
/// <inheritdoc />
public async Task<SubscriptionDetailInfo?> GetDetailAsync(long subscriptionId, CancellationToken cancellationToken = default)
public async Task<SubscriptionDetailInfo?> GetDetailAsync(
long subscriptionId,
CancellationToken cancellationToken = default,
bool ignoreTenantFilter = false)
{
var result = await dbContext.TenantSubscriptions
var subscriptionQuery = ignoreTenantFilter
? dbContext.TenantSubscriptions.IgnoreQueryFilters()
: dbContext.TenantSubscriptions;
var result = await subscriptionQuery
.AsNoTracking()
.Where(s => s.Id == subscriptionId)
.Select(s => new
@@ -147,10 +171,16 @@ public sealed class EfSubscriptionRepository(TakeoutAppDbContext dbContext) : IS
/// <inheritdoc />
public async Task<IReadOnlyList<SubscriptionWithTenant>> FindByIdsWithTenantAsync(
IEnumerable<long> subscriptionIds,
CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default,
bool ignoreTenantFilter = false)
{
var ids = subscriptionIds.ToList();
return await dbContext.TenantSubscriptions
var query = ignoreTenantFilter
? dbContext.TenantSubscriptions.IgnoreQueryFilters()
: dbContext.TenantSubscriptions;
return await query
.Where(s => ids.Contains(s.Id))
.Join(
dbContext.Tenants,
@@ -169,10 +199,15 @@ public sealed class EfSubscriptionRepository(TakeoutAppDbContext dbContext) : IS
public async Task<IReadOnlyList<AutoRenewalCandidate>> FindAutoRenewalCandidatesAsync(
DateTime now,
DateTime renewalThreshold,
CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default,
bool ignoreTenantFilter = false)
{
// 1. 查询开启自动续费且即将到期的活跃订阅
var query = dbContext.TenantSubscriptions
var subscriptionQuery = ignoreTenantFilter
? dbContext.TenantSubscriptions.IgnoreQueryFilters()
: dbContext.TenantSubscriptions;
var query = subscriptionQuery
.Where(s => s.Status == SubscriptionStatus.Active
&& s.AutoRenew
&& s.EffectiveTo <= renewalThreshold
@@ -195,10 +230,15 @@ public sealed class EfSubscriptionRepository(TakeoutAppDbContext dbContext) : IS
public async Task<IReadOnlyList<RenewalReminderCandidate>> FindRenewalReminderCandidatesAsync(
DateTime startOfDay,
DateTime endOfDay,
CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default,
bool ignoreTenantFilter = false)
{
// 1. 查询到期落在指定区间的订阅(且未开启自动续费)
var query = dbContext.TenantSubscriptions
var subscriptionQuery = ignoreTenantFilter
? dbContext.TenantSubscriptions.IgnoreQueryFilters()
: dbContext.TenantSubscriptions;
var query = subscriptionQuery
.Where(s => s.Status == SubscriptionStatus.Active
&& !s.AutoRenew
&& s.EffectiveTo >= startOfDay
@@ -226,10 +266,15 @@ public sealed class EfSubscriptionRepository(TakeoutAppDbContext dbContext) : IS
/// <inheritdoc />
public async Task<IReadOnlyList<TenantSubscription>> FindExpiredActiveSubscriptionsAsync(
DateTime now,
CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default,
bool ignoreTenantFilter = false)
{
var query = ignoreTenantFilter
? dbContext.TenantSubscriptions.IgnoreQueryFilters()
: dbContext.TenantSubscriptions;
// 1. 查询已到期仍为 Active 的订阅
return await dbContext.TenantSubscriptions
return await query
.Where(s => s.Status == SubscriptionStatus.Active && s.EffectiveTo < now)
.ToListAsync(cancellationToken);
}
@@ -238,10 +283,15 @@ public sealed class EfSubscriptionRepository(TakeoutAppDbContext dbContext) : IS
public async Task<IReadOnlyList<TenantSubscription>> FindGracePeriodExpiredSubscriptionsAsync(
DateTime now,
int gracePeriodDays,
CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default,
bool ignoreTenantFilter = false)
{
var query = ignoreTenantFilter
? dbContext.TenantSubscriptions.IgnoreQueryFilters()
: dbContext.TenantSubscriptions;
// 1. 查询宽限期已结束的订阅
return await dbContext.TenantSubscriptions
return await query
.Where(s => s.Status == SubscriptionStatus.GracePeriod
&& s.EffectiveTo.AddDays(gracePeriodDays) < now)
.ToListAsync(cancellationToken);
@@ -312,9 +362,14 @@ public sealed class EfSubscriptionRepository(TakeoutAppDbContext dbContext) : IS
/// <inheritdoc />
public async Task<IReadOnlyList<TenantQuotaUsage>> GetQuotaUsagesAsync(
long tenantId,
CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default,
bool ignoreTenantFilter = false)
{
return await dbContext.TenantQuotaUsages
var query = ignoreTenantFilter
? dbContext.TenantQuotaUsages.IgnoreQueryFilters()
: dbContext.TenantQuotaUsages;
return await query
.AsNoTracking()
.Where(q => q.TenantId == tenantId)
.ToListAsync(cancellationToken);