using MediatR; using TakeoutSaaS.Application.App.Tenants.Commands; using TakeoutSaaS.Application.App.Tenants.Dto; using TakeoutSaaS.Domain.Tenants.Entities; using TakeoutSaaS.Domain.Tenants.Enums; using TakeoutSaaS.Domain.Tenants.Repositories; using TakeoutSaaS.Shared.Abstractions.Constants; using TakeoutSaaS.Shared.Abstractions.Exceptions; using TakeoutSaaS.Shared.Abstractions.Ids; namespace TakeoutSaaS.Application.App.Tenants.Handlers; /// /// 套餐升降配处理器。 /// public sealed class ChangeTenantSubscriptionPlanCommandHandler( ITenantRepository tenantRepository, IIdGenerator idGenerator) : IRequestHandler { /// public async Task Handle(ChangeTenantSubscriptionPlanCommand request, CancellationToken cancellationToken) { // 1. 校验租户与订阅存在性 _ = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在"); var subscription = await tenantRepository.FindSubscriptionByIdAsync(request.TenantId, request.TenantSubscriptionId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "订阅不存在"); var previousPackage = subscription.TenantPackageId; // 2. 根据立即生效或排期设置目标套餐 if (request.Immediate) { subscription.TenantPackageId = request.TargetPackageId; subscription.ScheduledPackageId = null; } else { subscription.ScheduledPackageId = request.TargetPackageId; } // 3. 更新订阅并记录变更历史 await tenantRepository.UpdateSubscriptionAsync(subscription, cancellationToken); await tenantRepository.AddSubscriptionHistoryAsync(new TenantSubscriptionHistory { Id = idGenerator.NextId(), TenantId = subscription.TenantId, TenantSubscriptionId = subscription.Id, FromPackageId = previousPackage, ToPackageId = request.TargetPackageId, ChangeType = SubscriptionChangeType.Upgrade, EffectiveFrom = subscription.EffectiveFrom, EffectiveTo = subscription.EffectiveTo, Notes = request.Notes }, cancellationToken); // 4. 记录审计日志 await tenantRepository.AddAuditLogAsync(new TenantAuditLog { TenantId = subscription.TenantId, Action = TenantAuditAction.SubscriptionPlanChanged, Title = request.Immediate ? "套餐立即变更" : "套餐排期变更", Description = request.Notes, PreviousStatus = null, CurrentStatus = null }, cancellationToken); // 5. 保存并返回 DTO await tenantRepository.SaveChangesAsync(cancellationToken); return subscription.ToSubscriptionDto() ?? throw new BusinessException(ErrorCodes.InternalServerError, "订阅更新失败"); } }