using MediatR; using TakeoutSaaS.Application.App.Subscriptions.Dto; using TakeoutSaaS.Application.App.Subscriptions.Queries; using TakeoutSaaS.Application.App.Tenants; using TakeoutSaaS.Domain.Tenants.Repositories; using TakeoutSaaS.Domain.Tenants.Enums; namespace TakeoutSaaS.Application.App.Subscriptions.Handlers; /// /// 订阅详情查询处理器。 /// public sealed class GetSubscriptionDetailQueryHandler( ISubscriptionRepository subscriptionRepository) : IRequestHandler { /// public async Task Handle(GetSubscriptionDetailQuery request, CancellationToken cancellationToken) { // 1. 查询订阅基础信息 var detail = await subscriptionRepository.GetDetailAsync( request.SubscriptionId, cancellationToken, includeDeleted: request.IncludeDeleted); if (detail == null) { return null; } // 2. 查询配额使用情况 var quotaUsages = await subscriptionRepository.GetQuotaUsagesAsync( detail.Subscription.TenantId, cancellationToken, includeDeleted: request.IncludeDeleted); var quotaUsageDtos = BuildQuotaUsageDtos(detail.Package, quotaUsages); // 3. 查询订阅变更历史(关联套餐信息) var histories = await subscriptionRepository.GetHistoryAsync(request.SubscriptionId, cancellationToken); var historyDtos = histories.Select(h => new SubscriptionHistoryDto { Id = h.History.Id, TenantSubscriptionId = h.History.TenantSubscriptionId, FromPackageId = h.History.FromPackageId, FromPackageName = h.FromPackageName, ToPackageId = h.History.ToPackageId, ToPackageName = h.ToPackageName, ChangeType = h.History.ChangeType, EffectiveFrom = h.History.EffectiveFrom, EffectiveTo = h.History.EffectiveTo, Amount = h.History.Amount, Currency = h.History.Currency, Notes = h.History.Notes, CreatedAt = h.History.CreatedAt }).ToList(); // 4. 构建返回结果 return new SubscriptionDetailDto { Id = detail.Subscription.Id, TenantId = detail.Subscription.TenantId, TenantName = detail.TenantName, TenantCode = detail.TenantCode, TenantPackageId = detail.Subscription.TenantPackageId, Package = detail.Package?.ToDto(), ScheduledPackageId = detail.Subscription.ScheduledPackageId, ScheduledPackage = detail.ScheduledPackage?.ToDto(), Status = detail.Subscription.Status, EffectiveFrom = detail.Subscription.EffectiveFrom, EffectiveTo = detail.Subscription.EffectiveTo, NextBillingDate = detail.Subscription.NextBillingDate, AutoRenew = detail.Subscription.AutoRenew, Notes = detail.Subscription.Notes, QuotaUsages = quotaUsageDtos, ChangeHistory = historyDtos, CreatedAt = detail.Subscription.CreatedAt, UpdatedAt = detail.Subscription.UpdatedAt }; } private static List BuildQuotaUsageDtos( TakeoutSaaS.Domain.Tenants.Entities.TenantPackage? package, IReadOnlyList quotaUsages) { var usageByType = quotaUsages .GroupBy(u => u.QuotaType) .ToDictionary(g => g.Key, g => g.First()); var baselineTypes = new List<(TenantQuotaType Type, decimal LimitValue)>(); if (package != null) { baselineTypes.Add((TenantQuotaType.StoreCount, package.MaxStoreCount.HasValue ? package.MaxStoreCount.Value : 0)); baselineTypes.Add((TenantQuotaType.AccountCount, package.MaxAccountCount.HasValue ? package.MaxAccountCount.Value : 0)); baselineTypes.Add((TenantQuotaType.Storage, package.MaxStorageGb.HasValue ? package.MaxStorageGb.Value : 0)); baselineTypes.Add((TenantQuotaType.SmsCredits, package.MaxSmsCredits.HasValue ? package.MaxSmsCredits.Value : 0)); baselineTypes.Add((TenantQuotaType.DeliveryOrders, package.MaxDeliveryOrders.HasValue ? package.MaxDeliveryOrders.Value : 0)); } var results = new List(); foreach (var (type, limitValue) in baselineTypes) { usageByType.TryGetValue(type, out var usage); results.Add(new QuotaUsageDto { Id = usage?.Id ?? 0, QuotaType = type, LimitValue = limitValue, UsedValue = usage?.UsedValue ?? 0, ResetCycle = usage?.ResetCycle, LastResetAt = usage?.LastResetAt }); } // 补充套餐字段未覆盖的配额使用项(例如营销槽位等扩展配额) foreach (var usage in usageByType.Values) { if (baselineTypes.Any(x => x.Type == usage.QuotaType)) { continue; } results.Add(new QuotaUsageDto { Id = usage.Id, QuotaType = usage.QuotaType, LimitValue = usage.LimitValue, UsedValue = usage.UsedValue, ResetCycle = usage.ResetCycle, LastResetAt = usage.LastResetAt }); } return results .OrderBy(x => (int)x.QuotaType) .ToList(); } }