Files
TakeoutSaaS.AdminApi/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfStatisticsRepository.cs

117 lines
3.8 KiB
C#

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;
/// <summary>
/// 统计数据仓储实现。
/// </summary>
public sealed class EfStatisticsRepository(TakeoutAdminDbContext dbContext) : IStatisticsRepository
{
#region
/// <inheritdoc />
public async Task<IReadOnlyList<TenantSubscription>> GetAllSubscriptionsAsync(CancellationToken cancellationToken = default)
{
return await dbContext.TenantSubscriptions
.AsNoTracking()
.ToListAsync(cancellationToken);
}
/// <inheritdoc />
public async Task<IReadOnlyList<ExpiringSubscriptionInfo>> 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
/// <inheritdoc />
public async Task<IReadOnlyList<TenantBillingStatement>> GetPaidBillsAsync(CancellationToken cancellationToken = default)
{
return await dbContext.TenantBillingStatements
.AsNoTracking()
.Where(b => b.Status == TenantBillingStatus.Paid)
.ToListAsync(cancellationToken);
}
#endregion
#region 使
/// <inheritdoc />
public async Task<IReadOnlyList<QuotaUsageRankInfo>> 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
}