forked from xiongyuxing/tiku-backend.net
feat(saas): implement marketplace and tenant onboarding
This commit is contained in:
@@ -7,76 +7,6 @@ using Tiku.Domain.Tenancy;
|
||||
|
||||
namespace Tiku.Infrastructure.Persistence.Configurations;
|
||||
|
||||
internal sealed class PlatformSaasPlanConfiguration : IEntityTypeConfiguration<PlatformSaasPlan>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PlatformSaasPlan> builder)
|
||||
{
|
||||
builder.ConfigureEntity("platform_saas_plans");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.Code).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Name).HasMaxLength(200);
|
||||
builder.Property(entity => entity.BillingCycle).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Currency).HasMaxLength(10);
|
||||
builder.Property(entity => entity.IncludedQuotas).IsJson("{}");
|
||||
builder.Property(entity => entity.OveragePrices).IsJson("{}");
|
||||
builder.Property(entity => entity.FeatureFlags).IsJson("{}");
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
builder.HasIndex(entity => entity.Code).IsUnique();
|
||||
builder.HasIndex(entity => new { entity.Status, entity.SortOrder });
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_platform_saas_plans_amount", "base_amount_cents >= 0"));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ProductModuleConfiguration : IEntityTypeConfiguration<ProductModule>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<ProductModule> builder)
|
||||
{
|
||||
builder.ConfigureEntity("product_modules");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.Code).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Name).HasMaxLength(200);
|
||||
builder.Property(entity => entity.Description).HasMaxLength(1000);
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
builder.HasIndex(entity => entity.Code).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PlanModuleEntitlementConfiguration : IEntityTypeConfiguration<PlanModuleEntitlement>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PlanModuleEntitlement> builder)
|
||||
{
|
||||
builder.ConfigureEntity("plan_module_entitlements");
|
||||
builder.Property(entity => entity.PlanCode).HasMaxLength(100);
|
||||
builder.Property(entity => entity.ModuleCode).HasMaxLength(100);
|
||||
builder.HasIndex(entity => new { entity.PlanCode, entity.ModuleCode }).IsUnique();
|
||||
builder.HasOne<PlatformSaasPlan>().WithMany()
|
||||
.HasPrincipalKey(entity => entity.Code)
|
||||
.HasForeignKey(entity => entity.PlanCode)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<ProductModule>().WithMany()
|
||||
.HasPrincipalKey(entity => entity.Code)
|
||||
.HasForeignKey(entity => entity.ModuleCode)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantModuleOverrideConfiguration : IEntityTypeConfiguration<TenantModuleOverride>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantModuleOverride> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("tenant_module_overrides");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.ModuleCode).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Mode).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Reason).HasMaxLength(1000);
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.ModuleCode }).IsUnique();
|
||||
builder.HasOne<ProductModule>().WithMany()
|
||||
.HasPrincipalKey(entity => entity.Code)
|
||||
.HasForeignKey(entity => entity.ModuleCode)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantBillingProfileConfiguration : IEntityTypeConfiguration<TenantBillingProfile>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantBillingProfile> builder)
|
||||
@@ -98,84 +28,11 @@ internal sealed class TenantBillingProfileConfiguration : IEntityTypeConfigurati
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantInvoiceConfiguration : IEntityTypeConfiguration<TenantInvoice>
|
||||
internal sealed class PlatformBillingInvoiceReminderConfiguration : IEntityTypeConfiguration<PlatformBillingInvoiceReminder>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantInvoice> builder)
|
||||
public void Configure(EntityTypeBuilder<PlatformBillingInvoiceReminder> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("tenant_invoices");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.InvoiceNo).HasMaxLength(100);
|
||||
builder.Property(entity => entity.InvoiceType).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Currency).HasMaxLength(10);
|
||||
builder.Property(entity => entity.Metadata).IsJson("{}");
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.InvoiceNo }).IsUnique();
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.Status, entity.DueDate });
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.BillingPeriodStart, entity.BillingPeriodEnd })
|
||||
.IsUnique()
|
||||
.HasFilter("invoice_type = 'usage_overage' and status <> 'void' and metadata->>'source' = 'usage_overage_auto'");
|
||||
builder.ToTable(table =>
|
||||
{
|
||||
table.HasCheckConstraint("ck_tenant_invoices_amounts", "subtotal_cents >= 0 and discount_cents >= 0 and tax_cents >= 0 and total_cents >= 0 and paid_cents >= 0 and balance_cents >= 0");
|
||||
table.HasCheckConstraint("ck_tenant_invoices_period", "billing_period_end is null or billing_period_start is null or billing_period_end >= billing_period_start");
|
||||
});
|
||||
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.CreatedBy).OnDelete(DeleteBehavior.SetNull);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantInvoiceItemConfiguration : IEntityTypeConfiguration<TenantInvoiceItem>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantInvoiceItem> builder)
|
||||
{
|
||||
builder.ConfigureEntity("tenant_invoice_items");
|
||||
builder.HasAlternateKey(entity => new { entity.TenantId, entity.Id });
|
||||
builder.Property(entity => entity.ItemType).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Description).HasMaxLength(500);
|
||||
builder.Property(entity => entity.Quantity).HasPrecision(12, 2);
|
||||
builder.Property(entity => entity.Metadata).IsJson("{}");
|
||||
builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()");
|
||||
builder.HasIndex(entity => entity.InvoiceId);
|
||||
builder.ToTable(table =>
|
||||
{
|
||||
table.HasCheckConstraint("ck_tenant_invoice_items_quantity", "quantity > 0");
|
||||
table.HasCheckConstraint("ck_tenant_invoice_items_amounts", "unit_amount_cents >= 0 and amount_cents >= 0");
|
||||
});
|
||||
builder.HasOne<Tenant>().WithMany().HasForeignKey(entity => entity.TenantId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<TenantInvoice>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.InvoiceId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantInvoicePaymentConfiguration : IEntityTypeConfiguration<TenantInvoicePayment>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantInvoicePayment> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("tenant_invoice_payments");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.PaymentNo).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Provider).HasMaxLength(50);
|
||||
builder.Property(entity => entity.Method).HasMaxLength(50);
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.ProviderTradeNo).HasMaxLength(200);
|
||||
builder.Property(entity => entity.RawPayload).IsJson("{}");
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.PaymentNo }).IsUnique();
|
||||
builder.HasIndex(entity => new { entity.InvoiceId, entity.Status });
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_tenant_invoice_payments_amount", "amount_cents >= 0"));
|
||||
builder.HasOne<TenantInvoice>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.InvoiceId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.ReceivedBy).OnDelete(DeleteBehavior.SetNull);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantInvoiceReminderConfiguration : IEntityTypeConfiguration<TenantInvoiceReminder>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantInvoiceReminder> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("tenant_invoice_reminders");
|
||||
builder.ConfigureTenantEntity("platform_billing_invoice_reminders");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.ReminderType).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Channel).HasSnakeCaseEnum();
|
||||
@@ -186,10 +43,10 @@ internal sealed class TenantInvoiceReminderConfiguration : IEntityTypeConfigurat
|
||||
builder.HasIndex(entity => new { entity.InvoiceId, entity.ReminderDate });
|
||||
builder.ToTable(table =>
|
||||
{
|
||||
table.HasCheckConstraint("ck_tenant_invoice_reminders_level", "reminder_level between 1 and 20");
|
||||
table.HasCheckConstraint("ck_tenant_invoice_reminders_balance", "balance_cents_snapshot >= 0");
|
||||
table.HasCheckConstraint("ck_platform_billing_invoice_reminders_level", "reminder_level between 1 and 20");
|
||||
table.HasCheckConstraint("ck_platform_billing_invoice_reminders_balance", "balance_cents_snapshot >= 0");
|
||||
});
|
||||
builder.HasOne<TenantInvoice>().WithMany()
|
||||
builder.HasOne<PlatformBillingInvoice>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.InvoiceId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
@@ -243,11 +100,11 @@ internal sealed class PlatformAuditAlertConfiguration : IEntityTypeConfiguration
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PlatformDunningNotificationChannelConfiguration : IEntityTypeConfiguration<PlatformDunningNotificationChannel>
|
||||
internal sealed class PlatformBillingDunningNotificationChannelConfiguration : IEntityTypeConfiguration<PlatformBillingDunningNotificationChannel>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PlatformDunningNotificationChannel> builder)
|
||||
public void Configure(EntityTypeBuilder<PlatformBillingDunningNotificationChannel> builder)
|
||||
{
|
||||
builder.ConfigureEntity("platform_dunning_notification_channels");
|
||||
builder.ConfigureEntity("platform_billing_dunning_notification_channels");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.ChannelCode).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Name).HasMaxLength(200);
|
||||
@@ -262,17 +119,17 @@ internal sealed class PlatformDunningNotificationChannelConfiguration : IEntityT
|
||||
builder.HasIndex(entity => new { entity.Enabled, entity.MinReminderLevel, entity.ChannelCode });
|
||||
builder.ToTable(table =>
|
||||
{
|
||||
table.HasCheckConstraint("ck_platform_dunning_channels_level", "min_reminder_level between 1 and 20");
|
||||
table.HasCheckConstraint("ck_platform_dunning_channels_timeout", "timeout_seconds between 1 and 60");
|
||||
table.HasCheckConstraint("ck_platform_billing_dunning_channels_level", "min_reminder_level between 1 and 20");
|
||||
table.HasCheckConstraint("ck_platform_billing_dunning_channels_timeout", "timeout_seconds between 1 and 60");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PlatformDunningNotificationEventConfiguration : IEntityTypeConfiguration<PlatformDunningNotificationEvent>
|
||||
internal sealed class PlatformBillingDunningNotificationEventConfiguration : IEntityTypeConfiguration<PlatformBillingDunningNotificationEvent>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PlatformDunningNotificationEvent> builder)
|
||||
public void Configure(EntityTypeBuilder<PlatformBillingDunningNotificationEvent> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("platform_dunning_notification_events");
|
||||
builder.ConfigureTenantEntity("platform_billing_dunning_notification_events");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.Provider).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
@@ -284,15 +141,20 @@ internal sealed class PlatformDunningNotificationEventConfiguration : IEntityTyp
|
||||
builder.HasIndex(entity => new { entity.ReminderId, entity.Status, entity.CreatedAt });
|
||||
builder.HasIndex(entity => new { entity.InvoiceId, entity.Status, entity.CreatedAt });
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.Status, entity.CreatedAt });
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_platform_dunning_events_attempts", "attempts >= 0"));
|
||||
builder.HasOne<PlatformDunningNotificationChannel>().WithMany().HasForeignKey(entity => entity.ChannelId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<TenantInvoiceReminder>().WithMany()
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_platform_billing_dunning_events_attempts", "attempts >= 0"));
|
||||
builder.HasOne<PlatformBillingDunningNotificationChannel>().WithMany()
|
||||
.HasForeignKey(entity => entity.ChannelId)
|
||||
.HasConstraintName("fk_platform_billing_dunning_events_channel")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<PlatformBillingInvoiceReminder>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.ReminderId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.HasConstraintName("fk_platform_billing_dunning_events_reminder")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<TenantInvoice>().WithMany()
|
||||
builder.HasOne<PlatformBillingInvoice>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.InvoiceId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.HasConstraintName("fk_platform_billing_dunning_events_invoice")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user