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); } }