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