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;
///
/// 租户套餐仓储实现(AdminApi 使用)。
///
public sealed class EfTenantPackageRepository(TakeoutAdminDbContext context) : ITenantPackageRepository
{
///
public async Task GetByIdAsync(long tenantPackageId, CancellationToken cancellationToken = default)
{
// 1. 查询套餐(排除已删除,无跟踪)
return await context.TenantPackages
.AsNoTracking()
.Where(p => p.Id == tenantPackageId && p.DeletedAt == null)
.FirstOrDefaultAsync(cancellationToken);
}
///
public async Task GetByIdForUpdateAsync(long tenantPackageId, CancellationToken cancellationToken = default)
{
// 1. 查询套餐(排除已删除,带跟踪用于更新)
return await context.TenantPackages
.Where(p => p.Id == tenantPackageId && p.DeletedAt == null)
.FirstOrDefaultAsync(cancellationToken);
}
///
public async Task<(IReadOnlyList Items, int TotalCount)> GetListAsync(
string? keyword,
bool? isActive,
int page,
int pageSize,
CancellationToken cancellationToken = default)
{
// 1. 构建基础查询
var query = context.TenantPackages
.AsNoTracking()
.Where(p => p.DeletedAt == null);
// 2. 应用关键字过滤
if (!string.IsNullOrWhiteSpace(keyword))
{
var normalized = keyword.Trim();
query = query.Where(p => p.Name.Contains(normalized));
}
// 3. 应用启用状态过滤
if (isActive.HasValue)
{
query = query.Where(p => p.IsActive == isActive.Value);
}
// 4. 获取总数
var totalCount = await query.CountAsync(cancellationToken);
// 5. 分页查询(按排序序号升序)
var items = await query
.OrderBy(p => p.SortOrder)
.ThenBy(p => p.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync(cancellationToken);
// 6. 返回结果
return (items, totalCount);
}
///
public async Task> GetUsagesAsync(
IReadOnlyList tenantPackageIds,
CancellationToken cancellationToken = default)
{
// 1. 如果没有传入套餐 ID,返回空列表
if (tenantPackageIds.Count == 0)
{
return [];
}
// 2. 获取当前时间用于计算到期天数
var now = DateTime.UtcNow;
var in7Days = now.AddDays(7);
var in15Days = now.AddDays(15);
var in30Days = now.AddDays(30);
// 3. 查询订阅数据并按套餐分组统计
var subscriptions = await context.TenantSubscriptions
.AsNoTracking()
.Where(s => tenantPackageIds.Contains(s.TenantPackageId) && s.DeletedAt == null)
.Select(s => new
{
s.TenantPackageId,
s.TenantId,
s.Status,
s.EffectiveTo,
MonthlyPrice = context.TenantPackages
.Where(p => p.Id == s.TenantPackageId)
.Select(p => p.MonthlyPrice)
.FirstOrDefault()
})
.ToListAsync(cancellationToken);
// 4. 按套餐 ID 分组统计
var results = tenantPackageIds.Select(packageId =>
{
var packageSubscriptions = subscriptions.Where(s => s.TenantPackageId == packageId).ToList();
// 4.1 活跃订阅(状态为 Active 且未过期)
var activeSubscriptions = packageSubscriptions
.Where(s => s.Status == SubscriptionStatus.Active && s.EffectiveTo > now)
.ToList();
// 4.2 活跃租户数(去重)
var activeTenantCount = activeSubscriptions.Select(s => s.TenantId).Distinct().Count();
// 4.3 总订阅数
var totalSubscriptionCount = packageSubscriptions.Count;
// 4.4 计算 MRR(月度经常性收入)
var mrr = activeSubscriptions.Sum(s => s.MonthlyPrice ?? 0);
// 4.5 计算 ARR(年度经常性收入)
var arr = mrr * 12;
// 4.6 计算到期租户数(基于活跃订阅)
var expiringIn7Days = activeSubscriptions.Count(s => s.EffectiveTo <= in7Days);
var expiringIn15Days = activeSubscriptions.Count(s => s.EffectiveTo <= in15Days);
var expiringIn30Days = activeSubscriptions.Count(s => s.EffectiveTo <= in30Days);
return new TenantPackageUsageResult(
packageId,
activeSubscriptions.Count,
activeTenantCount,
totalSubscriptionCount,
mrr,
arr,
expiringIn7Days,
expiringIn15Days,
expiringIn30Days);
}).ToList();
// 5. 返回结果
return results;
}
///
public async Task AddAsync(TenantPackage package, CancellationToken cancellationToken = default)
{
// 1. 添加套餐实体
await context.TenantPackages.AddAsync(package, cancellationToken);
}
///
public Task SoftDeleteAsync(TenantPackage package, CancellationToken cancellationToken = default)
{
// 1. 设置软删除时间
package.DeletedAt = DateTime.UtcNow;
// 2. 返回已完成任务
return Task.CompletedTask;
}
///
public async Task SaveChangesAsync(CancellationToken cancellationToken = default)
{
// 1. 保存变更
await context.SaveChangesAsync(cancellationToken);
}
///
public async Task<(IReadOnlyList Items, int TotalCount)> GetTenantsAsync(
long tenantPackageId,
string? keyword,
int? expiringWithinDays,
int page,
int pageSize,
CancellationToken cancellationToken = default)
{
// 1. 获取当前时间
var now = DateTime.UtcNow;
// 2. 构建基础查询:活跃订阅(状态为 Active 且未过期)
var query = from s in context.TenantSubscriptions.AsNoTracking()
join t in context.Tenants.AsNoTracking() on s.TenantId equals t.Id
where s.TenantPackageId == tenantPackageId
&& s.DeletedAt == null
&& t.DeletedAt == null
&& s.Status == SubscriptionStatus.Active
&& s.EffectiveTo > now
select new { Subscription = s, Tenant = t };
// 3. 应用关键字过滤
if (!string.IsNullOrWhiteSpace(keyword))
{
var normalized = keyword.Trim();
query = query.Where(x => x.Tenant.Name.Contains(normalized) || x.Tenant.Code.Contains(normalized));
}
// 4. 应用到期天数筛选
if (expiringWithinDays.HasValue)
{
var expiryDate = now.AddDays(expiringWithinDays.Value);
query = query.Where(x => x.Subscription.EffectiveTo <= expiryDate);
}
// 5. 获取总数
var totalCount = await query.CountAsync(cancellationToken);
// 6. 分页查询(按到期时间升序,即将到期的排前面)
var items = await query
.OrderBy(x => x.Subscription.EffectiveTo)
.ThenBy(x => x.Tenant.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(x => new TenantPackageTenantResult(
x.Tenant.Id,
x.Tenant.Code,
x.Tenant.Name,
x.Tenant.Status,
x.Tenant.ContactName,
x.Tenant.ContactPhone,
x.Subscription.EffectiveFrom,
x.Subscription.EffectiveTo))
.ToListAsync(cancellationToken);
// 7. 返回结果
return (items, totalCount);
}
}