using Microsoft.EntityFrameworkCore; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Enums; using TakeoutSaaS.Domain.Tenants.Repositories; using TakeoutSaaS.Infrastructure.App.Persistence; namespace TakeoutSaaS.Infrastructure.App.Repositories; /// /// 统计数据仓储实现。 /// public sealed class EfStatisticsRepository(TakeoutAdminDbContext dbContext) : IStatisticsRepository { #region 订阅统计 /// public async Task> GetAllSubscriptionsAsync(CancellationToken cancellationToken = default) { return await dbContext.TenantSubscriptions .AsNoTracking() .ToListAsync(cancellationToken); } /// public async Task> GetExpiringSubscriptionsAsync( int daysAhead, bool onlyWithoutAutoRenew, CancellationToken cancellationToken = default) { var now = DateTime.UtcNow; var targetDate = now.AddDays(daysAhead); // 构建基础查询 var query = dbContext.TenantSubscriptions .AsNoTracking() .Where(s => s.Status == SubscriptionStatus.Active && s.EffectiveTo >= now && s.EffectiveTo <= targetDate); // 如果只查询未开启自动续费的 if (onlyWithoutAutoRenew) { query = query.Where(s => !s.AutoRenew); } // 连接租户和套餐信息 var result = await query .Join( dbContext.Tenants, sub => sub.TenantId, tenant => tenant.Id, (sub, tenant) => new { Subscription = sub, Tenant = tenant } ) .Join( dbContext.TenantPackages, combined => combined.Subscription.TenantPackageId, package => package.Id, (combined, package) => new ExpiringSubscriptionInfo { Subscription = combined.Subscription, TenantName = combined.Tenant.Name, PackageName = package.Name } ) .OrderBy(x => x.Subscription.EffectiveTo) .ToListAsync(cancellationToken); return result; } #endregion #region 收入统计 /// public async Task> GetPaidBillsAsync(CancellationToken cancellationToken = default) { return await dbContext.TenantBillingStatements .AsNoTracking() .Where(b => b.Status == TenantBillingStatus.Paid) .ToListAsync(cancellationToken); } #endregion #region 配额使用排行 /// public async Task> GetQuotaUsageRankingAsync( TenantQuotaType quotaType, int topN, CancellationToken cancellationToken = default) { return await dbContext.TenantQuotaUsages .AsNoTracking() .Where(q => q.QuotaType == quotaType && q.LimitValue > 0) .Join( dbContext.Tenants, quota => quota.TenantId, tenant => tenant.Id, (quota, tenant) => new QuotaUsageRankInfo { TenantId = quota.TenantId, TenantName = tenant.Name, UsedValue = quota.UsedValue, LimitValue = quota.LimitValue, UsagePercentage = quota.LimitValue > 0 ? (quota.UsedValue / quota.LimitValue * 100) : 0 } ) .OrderByDescending(x => x.UsagePercentage) .Take(topN) .ToListAsync(cancellationToken); } #endregion }