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 CreateTenantSubscriptionCommandHandler( ITenantRepository tenantRepository, IIdGenerator idGenerator) : IRequestHandler { /// public async Task Handle(CreateTenantSubscriptionCommand request, CancellationToken cancellationToken) { // 1. 校验订阅时长 if (request.DurationMonths <= 0) { throw new BusinessException(ErrorCodes.BadRequest, "订阅时长必须大于 0"); } // 2. 获取租户与当前订阅 var tenant = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken) ?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在"); var current = await tenantRepository.GetActiveSubscriptionAsync(request.TenantId, cancellationToken); var from = current?.EffectiveTo ?? tenant.EffectiveTo ?? DateTime.UtcNow; var effectiveFrom = from > DateTime.UtcNow ? from : DateTime.UtcNow; var effectiveTo = effectiveFrom.AddMonths(request.DurationMonths); // 3. 创建订阅实体 var subscription = new TenantSubscription { Id = idGenerator.NextId(), TenantId = tenant.Id, TenantPackageId = request.TenantPackageId, EffectiveFrom = effectiveFrom, EffectiveTo = effectiveTo, NextBillingDate = effectiveTo, Status = SubscriptionStatus.Active, AutoRenew = request.AutoRenew, Notes = request.Notes }; // 4. 记录订阅与历史 await tenantRepository.AddSubscriptionAsync(subscription, cancellationToken); await tenantRepository.AddSubscriptionHistoryAsync(new TenantSubscriptionHistory { Id = idGenerator.NextId(), TenantId = tenant.Id, TenantSubscriptionId = subscription.Id, FromPackageId = current?.TenantPackageId ?? request.TenantPackageId, ToPackageId = request.TenantPackageId, ChangeType = current == null ? SubscriptionChangeType.New : SubscriptionChangeType.Renew, EffectiveFrom = effectiveFrom, EffectiveTo = effectiveTo, Amount = null, Currency = null, Notes = request.Notes }, cancellationToken); // 5. 记录审计 await tenantRepository.AddAuditLogAsync(new TenantAuditLog { TenantId = tenant.Id, Action = TenantAuditAction.SubscriptionUpdated, Title = current == null ? "创建订阅" : "续费订阅", Description = $"套餐 {request.TenantPackageId} 时长 {request.DurationMonths} 月" }, cancellationToken); // 6. 保存变更 await tenantRepository.SaveChangesAsync(cancellationToken); // 7. 返回 DTO return subscription.ToSubscriptionDto() ?? throw new BusinessException(ErrorCodes.InternalServerError, "订阅生成失败"); } }