using MediatR; using Microsoft.Extensions.Logging; 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 RegisterTenantCommandHandler( ITenantRepository tenantRepository, IIdGenerator idGenerator, ILogger logger) : IRequestHandler { /// public async Task Handle(RegisterTenantCommand request, CancellationToken cancellationToken) { // 1. 校验订阅时长 if (request.DurationMonths <= 0) { throw new BusinessException(ErrorCodes.BadRequest, "订阅时长必须大于 0"); } // 2. 检查租户编码唯一性 if (await tenantRepository.ExistsByCodeAsync(request.Code, cancellationToken)) { throw new BusinessException(ErrorCodes.Conflict, $"租户编码 {request.Code} 已存在"); } // 3. 计算生效时间 var now = DateTime.UtcNow; var effectiveFrom = request.EffectiveFrom ?? now; var effectiveTo = effectiveFrom.AddMonths(request.DurationMonths); // 4. 构建租户实体 var tenant = new Tenant { Id = idGenerator.NextId(), Code = request.Code.Trim(), Name = request.Name, ShortName = request.ShortName, Industry = request.Industry, ContactName = request.ContactName, ContactPhone = request.ContactPhone, ContactEmail = request.ContactEmail, Status = TenantStatus.PendingReview, EffectiveFrom = effectiveFrom, EffectiveTo = effectiveTo }; // 5. 构建订阅实体 var subscription = new TenantSubscription { Id = idGenerator.NextId(), TenantId = tenant.Id, TenantPackageId = request.TenantPackageId, EffectiveFrom = effectiveFrom, EffectiveTo = effectiveTo, NextBillingDate = effectiveTo, Status = SubscriptionStatus.Pending, AutoRenew = request.AutoRenew, Notes = "Init subscription" }; // 6. 持久化租户、订阅和审计日志 await tenantRepository.AddTenantAsync(tenant, cancellationToken); await tenantRepository.AddSubscriptionAsync(subscription, cancellationToken); await tenantRepository.AddAuditLogAsync(new TenantAuditLog { TenantId = tenant.Id, Action = TenantAuditAction.RegistrationSubmitted, Title = "租户注册", Description = $"提交套餐 {request.TenantPackageId},时长 {request.DurationMonths} 月" }, cancellationToken); await tenantRepository.SaveChangesAsync(cancellationToken); // 7. 记录日志 logger.LogInformation("已注册租户 {TenantCode}", tenant.Code); // 8. 返回 DTO return TenantMapping.ToDto(tenant, subscription, null); } }