forked from xiongyuxing/tiku-backend.net
260 lines
15 KiB
C#
260 lines
15 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
|
using Tiku.Domain.Growth;
|
|
using Tiku.Domain.Identity;
|
|
using Tiku.Domain.Tenancy;
|
|
|
|
namespace Tiku.Infrastructure.Persistence.Configurations;
|
|
|
|
internal sealed class ReferralTrackConfiguration : IEntityTypeConfiguration<ReferralTrack>
|
|
{
|
|
public void Configure(EntityTypeBuilder<ReferralTrack> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("referral_tracks");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
|
builder.Property(entity => entity.EventType).HasMaxLength(100);
|
|
builder.Property(entity => entity.RefCode).HasColumnType("citext").HasMaxLength(100);
|
|
builder.Property(entity => entity.Source).HasMaxLength(100);
|
|
builder.Property(entity => entity.IpAddress).HasMaxLength(100);
|
|
builder.Property(entity => entity.UserAgent).HasMaxLength(1024);
|
|
builder.Property(entity => entity.Metadata).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LeadId, entity.CreatedAt });
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.RefCode, entity.EventType, entity.CreatedAt });
|
|
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.ReferrerUserId).OnDelete(DeleteBehavior.SetNull);
|
|
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.TargetUserId).OnDelete(DeleteBehavior.SetNull);
|
|
builder.HasOne<ReferralLead>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.LeadId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
}
|
|
}
|
|
|
|
internal sealed class ReferralCodeConfiguration : IEntityTypeConfiguration<ReferralCode>
|
|
{
|
|
public void Configure(EntityTypeBuilder<ReferralCode> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("referral_codes");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.Code).HasColumnType("citext").HasMaxLength(100);
|
|
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.Channel).HasMaxLength(100);
|
|
builder.Property(entity => entity.LandingPath).HasMaxLength(2048);
|
|
builder.Property(entity => entity.Metadata).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.UserId }).IsUnique();
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.Code }).IsUnique();
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.Code, entity.Status });
|
|
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.UserId).OnDelete(DeleteBehavior.Cascade);
|
|
}
|
|
}
|
|
|
|
internal sealed class ReferralLeadConfiguration : IEntityTypeConfiguration<ReferralLead>
|
|
{
|
|
public void Configure(EntityTypeBuilder<ReferralLead> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("referral_leads");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.RefCode).HasColumnType("citext").HasMaxLength(100);
|
|
builder.Property(entity => entity.Source).HasMaxLength(100);
|
|
builder.Property(entity => entity.BindType).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.AssignmentMode).HasNullableSnakeCaseEnum();
|
|
builder.Property(entity => entity.Metadata).IsJson("{}");
|
|
builder.Property(entity => entity.BoundAt).HasDefaultValueSql("now()");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.StudentUserId }).IsUnique();
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.ReferrerUserId, entity.BoundAt });
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.AssignedToUserId, entity.Status, entity.BoundAt });
|
|
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.StudentUserId).OnDelete(DeleteBehavior.Cascade);
|
|
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.ReferrerUserId).OnDelete(DeleteBehavior.SetNull);
|
|
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.AssignedToUserId).OnDelete(DeleteBehavior.SetNull);
|
|
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.AssignedBy).OnDelete(DeleteBehavior.SetNull);
|
|
builder.HasOne<ReferralTrack>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.FirstTrackId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
}
|
|
}
|
|
|
|
internal sealed class ReferralTeamEdgeConfiguration : IEntityTypeConfiguration<ReferralTeamEdge>
|
|
{
|
|
public void Configure(EntityTypeBuilder<ReferralTeamEdge> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("referral_team_edges");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.RelationType).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.Metadata).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.MemberUserId, entity.RelationType }).IsUnique();
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LeaderUserId, entity.Status });
|
|
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.MemberUserId).OnDelete(DeleteBehavior.Cascade);
|
|
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.LeaderUserId).OnDelete(DeleteBehavior.SetNull);
|
|
}
|
|
}
|
|
|
|
internal sealed class ReferralQrcodeConfiguration : IEntityTypeConfiguration<ReferralQrcode>
|
|
{
|
|
public void Configure(EntityTypeBuilder<ReferralQrcode> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("referral_qrcodes");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.RefCode).HasColumnType("citext").HasMaxLength(100);
|
|
builder.Property(entity => entity.Scene).HasMaxLength(300);
|
|
builder.Property(entity => entity.Page).HasMaxLength(300);
|
|
builder.Property(entity => entity.Provider).HasMaxLength(100);
|
|
builder.Property(entity => entity.QrcodeUrl).HasMaxLength(2048);
|
|
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.Metadata).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.Provider, entity.Scene, entity.Page }).IsUnique();
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.RefCode, entity.Status });
|
|
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.UserId).OnDelete(DeleteBehavior.SetNull);
|
|
}
|
|
}
|
|
|
|
internal sealed class CrmConfigConfiguration : IEntityTypeConfiguration<CrmConfig>
|
|
{
|
|
public void Configure(EntityTypeBuilder<CrmConfig> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("crm_config");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.Url).HasMaxLength(2048);
|
|
builder.Property(entity => entity.SecretRef).HasMaxLength(300);
|
|
builder.Property(entity => entity.FormName).HasMaxLength(200);
|
|
builder.Property(entity => entity.ExamType).HasMaxLength(100);
|
|
builder.Property(entity => entity.AssignmentMode).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.AssignmentPool).IsJson("[]");
|
|
builder.Property(entity => entity.AssignmentConfig).IsJson("{}");
|
|
builder.HasIndex(entity => entity.TenantId).IsUnique();
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.AssignmentMode });
|
|
}
|
|
}
|
|
|
|
internal sealed class CrmWebhookQueueItemConfiguration : IEntityTypeConfiguration<CrmWebhookQueueItem>
|
|
{
|
|
public void Configure(EntityTypeBuilder<CrmWebhookQueueItem> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("crm_webhook_queue");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
|
builder.Property(entity => entity.RecordId).HasMaxLength(200);
|
|
builder.Property(entity => entity.LeadId).HasMaxLength(200);
|
|
builder.Property(entity => entity.Source).HasMaxLength(200);
|
|
builder.Property(entity => entity.Provider).HasMaxLength(100);
|
|
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.IdempotencyKey).HasMaxLength(200);
|
|
builder.Property(entity => entity.TargetUrl).HasMaxLength(2048);
|
|
builder.Property(entity => entity.Payload).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.RecordId })
|
|
.IsUnique()
|
|
.HasFilter("record_id is not null");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.IdempotencyKey })
|
|
.IsUnique()
|
|
.HasFilter("idempotency_key is not null");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.Status, entity.ScheduledAt, entity.NextAttemptAt });
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.RecordId, entity.CreatedAt })
|
|
.HasFilter("record_id is not null");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.Source, entity.CreatedAt })
|
|
.HasFilter("source = 'tenant.student.crm_push'");
|
|
builder.ToTable(table => table.HasCheckConstraint("ck_crm_webhook_queue_attempts", "attempts >= 0"));
|
|
}
|
|
}
|
|
|
|
internal sealed class CrmWebhookLogConfiguration : IEntityTypeConfiguration<CrmWebhookLog>
|
|
{
|
|
public void Configure(EntityTypeBuilder<CrmWebhookLog> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("crm_webhook_log");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.LegacyId).HasMaxLength(64);
|
|
builder.Property(entity => entity.RecordId).HasMaxLength(200);
|
|
builder.Property(entity => entity.LeadId).HasMaxLength(200);
|
|
builder.Property(entity => entity.Outcome).HasMaxLength(100);
|
|
builder.Property(entity => entity.RequestPayload).IsJson("{}");
|
|
builder.Property(entity => entity.ResponseSummary).HasMaxLength(2000);
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.RecordId, entity.CreatedAt });
|
|
}
|
|
}
|
|
|
|
internal sealed class TenantCommissionSettingConfiguration : IEntityTypeConfiguration<TenantCommissionSetting>
|
|
{
|
|
public void Configure(EntityTypeBuilder<TenantCommissionSetting> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("tenant_commission_settings");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.DefaultRate).HasPrecision(6, 4);
|
|
builder.Property(entity => entity.SettlementCycle).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.Config).IsJson("{}");
|
|
builder.HasIndex(entity => entity.TenantId).IsUnique();
|
|
builder.ToTable(table =>
|
|
{
|
|
table.HasCheckConstraint("ck_tenant_commission_settings_default_rate", "default_rate >= 0 and default_rate <= 1");
|
|
table.HasCheckConstraint("ck_tenant_commission_settings_min_settlement", "min_settlement_cents >= 0");
|
|
});
|
|
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.UpdatedBy).OnDelete(DeleteBehavior.SetNull);
|
|
}
|
|
}
|
|
|
|
internal sealed class CommissionSettlementConfiguration : IEntityTypeConfiguration<CommissionSettlement>
|
|
{
|
|
public void Configure(EntityTypeBuilder<CommissionSettlement> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("commission_settlements");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.SettlementNo).HasMaxLength(100);
|
|
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.DefaultRate).HasPrecision(6, 4);
|
|
builder.Property(entity => entity.EffectiveRate).HasPrecision(6, 4);
|
|
builder.Property(entity => entity.PaymentMethod).HasMaxLength(100);
|
|
builder.Property(entity => entity.PaymentAccount).HasMaxLength(200);
|
|
builder.Property(entity => entity.Metadata).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.SettlementNo }).IsUnique();
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.ReferrerUserId, entity.PeriodStart, entity.PeriodEnd })
|
|
.IsUnique()
|
|
.HasDatabaseName("ux_commission_settlements_referrer_period");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.ReferrerUserId, entity.PeriodStart })
|
|
.HasDatabaseName("ix_commission_settlements_referrer_start");
|
|
builder.ToTable(table =>
|
|
{
|
|
table.HasCheckConstraint("ck_commission_settlements_counts", "source_count >= 0 and paid_user_count >= 0");
|
|
table.HasCheckConstraint("ck_commission_settlements_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0");
|
|
table.HasCheckConstraint("ck_commission_settlements_rates", "default_rate >= 0 and default_rate <= 1 and (effective_rate is null or (effective_rate >= 0 and effective_rate <= 1))");
|
|
table.HasCheckConstraint("ck_commission_settlements_period", "period_end >= period_start");
|
|
});
|
|
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.ReferrerUserId).OnDelete(DeleteBehavior.Cascade);
|
|
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.GeneratedBy).OnDelete(DeleteBehavior.SetNull);
|
|
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.ReviewedBy).OnDelete(DeleteBehavior.SetNull);
|
|
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.PaidBy).OnDelete(DeleteBehavior.SetNull);
|
|
}
|
|
}
|
|
|
|
internal sealed class CommissionSettlementItemConfiguration : IEntityTypeConfiguration<CommissionSettlementItem>
|
|
{
|
|
public void Configure(EntityTypeBuilder<CommissionSettlementItem> builder)
|
|
{
|
|
builder.ConfigureTenantEntity("commission_settlement_items");
|
|
builder.ConfigureTimestamps();
|
|
builder.Property(entity => entity.SourceType).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.SourceNo).HasMaxLength(100);
|
|
builder.Property(entity => entity.CommissionRate).HasPrecision(6, 4);
|
|
builder.Property(entity => entity.RateSource).HasSnakeCaseEnum();
|
|
builder.Property(entity => entity.AttributionType).HasMaxLength(100);
|
|
builder.Property(entity => entity.Metadata).IsJson("{}");
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.SourceType, entity.SourceId }).IsUnique();
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.ReferrerUserId, entity.SourcePaidAt });
|
|
builder.HasIndex(entity => new { entity.TenantId, entity.SettlementId });
|
|
builder.ToTable(table =>
|
|
{
|
|
table.HasCheckConstraint("ck_commission_settlement_items_amounts", "gross_amount_cents >= 0 and commission_amount_cents >= 0");
|
|
table.HasCheckConstraint("ck_commission_settlement_items_rate", "commission_rate >= 0 and commission_rate <= 1");
|
|
});
|
|
builder.HasOne<CommissionSettlement>().WithMany()
|
|
.HasForeignKey(entity => new { entity.TenantId, entity.SettlementId })
|
|
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.ReferrerUserId).OnDelete(DeleteBehavior.Cascade);
|
|
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.StudentUserId).OnDelete(DeleteBehavior.SetNull);
|
|
}
|
|
}
|