using System.Text.Json; using TakeoutSaaS.Application.App.Billings.Dto; using TakeoutSaaS.Domain.Tenants.Entities; namespace TakeoutSaaS.Application.App.Billings; /// /// 账单 DTO 映射助手。 /// internal static class BillingMapping { /// /// 将账单实体映射为账单 DTO(旧版)。 /// /// 账单实体。 /// 租户名称。 /// 账单 DTO。 public static BillDto ToDto(this TenantBillingStatement bill, string? tenantName = null) => new() { Id = bill.Id, TenantId = bill.TenantId, TenantName = tenantName, StatementNo = bill.StatementNo, PeriodStart = bill.PeriodStart, PeriodEnd = bill.PeriodEnd, AmountDue = bill.AmountDue, AmountPaid = bill.AmountPaid, Status = bill.Status, DueDate = bill.DueDate, CreatedAt = bill.CreatedAt }; /// /// 将账单实体映射为账单列表 DTO(新版)。 /// /// 账单实体。 /// 租户名称。 /// 账单列表 DTO。 public static BillingListDto ToBillingListDto(this TenantBillingStatement billing, string? tenantName = null) => new() { Id = billing.Id, TenantId = billing.TenantId, SubscriptionId = billing.SubscriptionId, TenantName = tenantName ?? string.Empty, StatementNo = billing.StatementNo, BillingType = billing.BillingType, Status = billing.Status, PeriodStart = billing.PeriodStart, PeriodEnd = billing.PeriodEnd, AmountDue = billing.AmountDue, AmountPaid = billing.AmountPaid, DiscountAmount = billing.DiscountAmount, TaxAmount = billing.TaxAmount, TotalAmount = billing.CalculateTotalAmount(), Currency = billing.Currency, DueDate = billing.DueDate, CreatedAt = billing.CreatedAt, UpdatedAt = billing.UpdatedAt, IsOverdue = billing.Status == TakeoutSaaS.Domain.Tenants.Enums.TenantBillingStatus.Overdue || (billing.Status == TakeoutSaaS.Domain.Tenants.Enums.TenantBillingStatus.Pending && billing.DueDate < DateTime.UtcNow), OverdueDays = (billing.Status is TakeoutSaaS.Domain.Tenants.Enums.TenantBillingStatus.Pending or TakeoutSaaS.Domain.Tenants.Enums.TenantBillingStatus.Overdue) && billing.DueDate < DateTime.UtcNow ? (int)(DateTime.UtcNow - billing.DueDate).TotalDays : 0 }; /// /// 将账单实体与支付记录映射为账单详情 DTO(旧版)。 /// /// 账单实体。 /// 支付记录列表。 /// 租户名称。 /// 账单详情 DTO。 public static BillDetailDto ToDetailDto( this TenantBillingStatement bill, List payments, string? tenantName = null) => new() { Id = bill.Id, TenantId = bill.TenantId, TenantName = tenantName, StatementNo = bill.StatementNo, PeriodStart = bill.PeriodStart, PeriodEnd = bill.PeriodEnd, AmountDue = bill.AmountDue, AmountPaid = bill.AmountPaid, Status = bill.Status, DueDate = bill.DueDate, LineItemsJson = bill.LineItemsJson, CreatedAt = bill.CreatedAt, Payments = payments.Select(p => p.ToDto()).ToList() }; /// /// 将账单实体与支付记录映射为账单详情 DTO(新版)。 /// /// 账单实体。 /// 支付记录列表。 /// 租户名称。 /// 账单详情 DTO。 public static BillingDetailDto ToBillingDetailDto( this TenantBillingStatement billing, List payments, string? tenantName = null) { // 反序列化账单明细 var lineItems = new List(); if (!string.IsNullOrWhiteSpace(billing.LineItemsJson)) { try { lineItems = JsonSerializer.Deserialize>(billing.LineItemsJson) ?? []; } catch { lineItems = []; } } return new BillingDetailDto { Id = billing.Id, TenantId = billing.TenantId, TenantName = tenantName ?? string.Empty, SubscriptionId = billing.SubscriptionId, StatementNo = billing.StatementNo, BillingType = billing.BillingType, Status = billing.Status, PeriodStart = billing.PeriodStart, PeriodEnd = billing.PeriodEnd, AmountDue = billing.AmountDue, AmountPaid = billing.AmountPaid, DiscountAmount = billing.DiscountAmount, TaxAmount = billing.TaxAmount, TotalAmount = billing.CalculateTotalAmount(), Currency = billing.Currency, DueDate = billing.DueDate, ReminderSentAt = billing.ReminderSentAt, OverdueNotifiedAt = billing.OverdueNotifiedAt, LineItemsJson = billing.LineItemsJson, LineItems = lineItems, Payments = payments.Select(p => p.ToPaymentRecordDto()).ToList(), Notes = billing.Notes, CreatedAt = billing.CreatedAt, CreatedBy = billing.CreatedBy, UpdatedAt = billing.UpdatedAt, UpdatedBy = billing.UpdatedBy }; } /// /// 将支付记录实体映射为支付 DTO(旧版)。 /// /// 支付记录实体。 /// 支付 DTO。 public static PaymentDto ToDto(this TenantPayment payment) => new() { Id = payment.Id, BillingStatementId = payment.BillingStatementId, Amount = payment.Amount, Method = payment.Method, Status = payment.Status, TransactionNo = payment.TransactionNo, ProofUrl = payment.ProofUrl, PaidAt = payment.PaidAt, Notes = payment.Notes, CreatedAt = payment.CreatedAt }; /// /// 将支付记录实体映射为支付记录 DTO(新版)。 /// /// 支付记录实体。 /// 支付记录 DTO。 public static PaymentRecordDto ToPaymentRecordDto(this TenantPayment payment) => new() { Id = payment.Id, TenantId = payment.TenantId, BillingId = payment.BillingStatementId, Amount = payment.Amount, Method = payment.Method, Status = payment.Status, TransactionNo = payment.TransactionNo, ProofUrl = payment.ProofUrl, IsVerified = payment.VerifiedAt.HasValue, PaidAt = payment.PaidAt, VerifiedBy = payment.VerifiedBy, VerifiedAt = payment.VerifiedAt, RefundReason = payment.RefundReason, RefundedAt = payment.RefundedAt, Notes = payment.Notes, CreatedAt = payment.CreatedAt }; }