using MediatR; using System.Text.Json; using TakeoutSaaS.Application.App.Billings.Commands; using TakeoutSaaS.Application.App.Billings.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.Billings.Handlers; /// /// 创建账单命令处理器。 /// public sealed class CreateBillingCommandHandler( ITenantRepository tenantRepository, ITenantBillingRepository billingRepository, IIdGenerator idGenerator) : IRequestHandler { /// public async Task Handle(CreateBillingCommand request, CancellationToken cancellationToken) { // 1. 校验租户存在 var tenant = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken); if (tenant is null) { throw new BusinessException(ErrorCodes.NotFound, "租户不存在"); } // 2. 构建账单实体 var now = DateTime.UtcNow; var statementNo = $"BIL-{now:yyyyMMdd}-{idGenerator.NextId()}"; var lineItemsJson = JsonSerializer.Serialize(request.LineItems); var billing = new TenantBillingStatement { TenantId = request.TenantId, StatementNo = statementNo, BillingType = request.BillingType, SubscriptionId = null, PeriodStart = now, PeriodEnd = now, AmountDue = request.AmountDue, DiscountAmount = 0m, TaxAmount = 0m, AmountPaid = 0m, Currency = "CNY", Status = TenantBillingStatus.Pending, DueDate = request.DueDate, LineItemsJson = lineItemsJson, Notes = request.Notes }; // 3. 持久化账单 await billingRepository.AddAsync(billing, cancellationToken); await billingRepository.SaveChangesAsync(cancellationToken); // 4. 返回详情 DTO return billing.ToBillingDetailDto([], tenant.Name); } }