feature: Schema, Migrations, Core CRUD, and Redis Threshold Cache

This commit is contained in:
voltsrage
2026-06-16 17:59:16 +08:00
commit 882d4af3e6
63 changed files with 3923 additions and 0 deletions
@@ -0,0 +1,61 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
public class ClinicalAlertConfiguration : IEntityTypeConfiguration<ClinicalAlert>
{
public void Configure(EntityTypeBuilder<ClinicalAlert> builder)
{
builder.ToTable("clinical_alerts", t =>
{
t.HasCheckConstraint("chk_clinical_alerts_severity",
"severity IN ('WARNING', 'CRITICAL')");
t.HasCheckConstraint("chk_clinical_alerts_status",
"status IN ('OPEN', 'ACKNOWLEDGED', 'RESOLVED', 'ESCALATED')");
t.HasCheckConstraint("chk_clinical_alerts_alert_type",
"alert_type IN ('SEPSIS_WARNING', 'CRITICAL_HEART_RATE', 'CRITICAL_TEMP_C', " +
"'CRITICAL_POTASSIUM_MEQ_L', 'CRITICAL_SPO2', 'CRITICAL_RESP_RATE', 'CRITICAL_WBC_K_UL')");
});
builder.HasKey(a => a.Id);
builder.Property(a => a.Id).HasColumnName("id").HasDefaultValueSql("gen_random_uuid()");
builder.Property(a => a.EncounterId).HasColumnName("encounter_id");
builder.Property(a => a.PatientId).HasColumnName("patient_id");
builder.Property(a => a.ObservationId).HasColumnName("observation_id");
builder.Property(a => a.AlertType)
.HasColumnName("alert_type")
.HasMaxLength(50)
.HasConversion(
v => v.ToDbString(),
v => AlertTypeExtensions.FromDbString(v))
.IsRequired();
builder.Property(a => a.Severity)
.HasColumnName("severity")
.HasMaxLength(20)
.HasConversion(
v => v.ToDbString(),
v => AlertSeverityExtensions.FromDbString(v))
.IsRequired();
builder.Property(a => a.Details).HasColumnName("details").IsRequired();
builder.Property(a => a.Status)
.HasColumnName("status")
.HasMaxLength(20)
.HasConversion(
v => v.ToDbString(),
v => AlertStatusExtensions.FromDbString(v))
.HasDefaultValueSql("'OPEN'")
.HasSentinel((AlertStatus)(-1));
builder.Property(a => a.AcknowledgedAt).HasColumnName("acknowledged_at");
builder.Property(a => a.AcknowledgedBy).HasColumnName("acknowledged_by").HasMaxLength(200);
builder.Property(a => a.ResolvedAt).HasColumnName("resolved_at");
builder.Property(a => a.TriggeredAt).HasColumnName("triggered_at").HasDefaultValueSql("NOW()");
builder.HasOne(a => a.Encounter)
.WithMany(e => e.Alerts)
.HasForeignKey(a => a.EncounterId)
.OnDelete(DeleteBehavior.Restrict);
builder.HasIndex(a => new { a.EncounterId, a.TriggeredAt });
builder.HasIndex(a => new { a.PatientId, a.TriggeredAt });
builder.HasIndex(a => new { a.Severity, a.TriggeredAt })
.HasFilter("status = 'OPEN'");
}
}