feature: Corrections and Supersession

This commit is contained in:
voltsrage
2026-06-26 16:33:31 +08:00
parent 706318e5d2
commit f232761fd7
35 changed files with 4907 additions and 75 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,102 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace VigilCareRecordsAPI.Data.Migrations
{
/// <inheritdoc />
public partial class AddLiveEncountersAndObservations : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "live_encounters",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
patient_id = table.Column<Guid>(type: "uuid", nullable: false),
admission_date = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
department = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
room_bed = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
admission_reason = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
discharge_diagnosis = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "NOW()")
},
constraints: table =>
{
table.PrimaryKey("PK_live_encounters", x => x.id);
table.CheckConstraint("chk_live_encounters_department", "department IS NULL OR department IN ('Emergency Department', 'Internal Medicine', 'General Medicine', 'Surgery', 'ICU', 'NICU', 'Medical-Surgical', 'Outpatient Clinic', 'Pediatrics', 'Obstetrics & Gynecology', 'Labor & Delivery', 'Cardiology', 'Orthopedics', 'Neurology', 'Oncology', 'Radiology', 'Laboratory', 'Psychiatry', 'Physical Therapy', 'Anesthesiology')");
table.ForeignKey(
name: "FK_live_encounters_patients_patient_id",
column: x => x.patient_id,
principalSchema: "clinical",
principalTable: "patients",
principalColumn: "id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "live_observations",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
encounter_id = table.Column<Guid>(type: "uuid", nullable: false),
patient_id = table.Column<Guid>(type: "uuid", nullable: true),
source_batch_id = table.Column<Guid>(type: "uuid", nullable: false),
observation_code = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
value = table.Column<decimal>(type: "numeric(10,3)", nullable: false),
unit = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
recorded_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
note = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "NOW()"),
is_superseded = table.Column<bool>(type: "boolean", nullable: false, defaultValue: false),
superseded_by_batch_id = table.Column<Guid>(type: "uuid", nullable: true),
superseded_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_live_observations", x => x.id);
table.ForeignKey(
name: "FK_live_observations_live_encounters_encounter_id",
column: x => x.encounter_id,
principalTable: "live_encounters",
principalColumn: "id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_live_encounters_patient_id_status",
table: "live_encounters",
columns: new[] { "patient_id", "status" });
migrationBuilder.CreateIndex(
name: "IX_live_observations_encounter_id_observation_code",
table: "live_observations",
columns: new[] { "encounter_id", "observation_code" });
migrationBuilder.CreateIndex(
name: "IX_live_observations_is_superseded",
table: "live_observations",
column: "is_superseded",
filter: "is_superseded = true");
migrationBuilder.CreateIndex(
name: "IX_live_observations_source_batch_id",
table: "live_observations",
column: "source_batch_id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "live_observations");
migrationBuilder.DropTable(
name: "live_encounters");
}
}
}
@@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace VigilCareRecordsAPI.Data.Migrations
{
/// <inheritdoc />
public partial class AddLiveObservationAndLiveEncounter : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
@@ -547,6 +547,141 @@ namespace VigilCareRecordsAPI.Data.Migrations
b.ToTable("idempotency_records", (string)null);
});
modelBuilder.Entity("LiveEncounter", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasColumnName("id")
.HasDefaultValueSql("gen_random_uuid()");
b.Property<DateTimeOffset>("AdmissionDate")
.HasColumnType("timestamp with time zone")
.HasColumnName("admission_date");
b.Property<string>("AdmissionReason")
.HasMaxLength(500)
.HasColumnType("character varying(500)")
.HasColumnName("admission_reason");
b.Property<DateTimeOffset>("CreatedAt")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasColumnName("created_at")
.HasDefaultValueSql("NOW()");
b.Property<string>("Department")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("department");
b.Property<string>("DischargeDiagnosis")
.HasMaxLength(500)
.HasColumnType("character varying(500)")
.HasColumnName("discharge_diagnosis");
b.Property<Guid>("PatientId")
.HasColumnType("uuid")
.HasColumnName("patient_id");
b.Property<string>("RoomBed")
.HasMaxLength(50)
.HasColumnType("character varying(50)")
.HasColumnName("room_bed");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)")
.HasColumnName("status");
b.HasKey("Id");
b.HasIndex("PatientId", "Status");
b.ToTable("live_encounters", null, t =>
{
t.HasCheckConstraint("chk_live_encounters_department", "department IS NULL OR department IN ('Emergency Department', 'Internal Medicine', 'General Medicine', 'Surgery', 'ICU', 'NICU', 'Medical-Surgical', 'Outpatient Clinic', 'Pediatrics', 'Obstetrics & Gynecology', 'Labor & Delivery', 'Cardiology', 'Orthopedics', 'Neurology', 'Oncology', 'Radiology', 'Laboratory', 'Psychiatry', 'Physical Therapy', 'Anesthesiology')");
});
});
modelBuilder.Entity("LiveObservation", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasColumnName("id")
.HasDefaultValueSql("gen_random_uuid()");
b.Property<DateTimeOffset>("CreatedAt")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasColumnName("created_at")
.HasDefaultValueSql("NOW()");
b.Property<Guid>("EncounterId")
.HasColumnType("uuid")
.HasColumnName("encounter_id");
b.Property<bool>("IsSuperseded")
.ValueGeneratedOnAdd()
.HasColumnType("boolean")
.HasDefaultValue(false)
.HasColumnName("is_superseded");
b.Property<string>("Note")
.HasMaxLength(500)
.HasColumnType("character varying(500)")
.HasColumnName("note");
b.Property<string>("ObservationCode")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)")
.HasColumnName("observation_code");
b.Property<Guid?>("PatientId")
.HasColumnType("uuid")
.HasColumnName("patient_id");
b.Property<DateTimeOffset>("RecordedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("recorded_at");
b.Property<Guid>("SourceBatchId")
.HasColumnType("uuid")
.HasColumnName("source_batch_id");
b.Property<DateTimeOffset?>("SupersededAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("superseded_at");
b.Property<Guid?>("SupersededByBatchId")
.HasColumnType("uuid")
.HasColumnName("superseded_by_batch_id");
b.Property<string>("Unit")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)")
.HasColumnName("unit");
b.Property<decimal>("Value")
.HasColumnType("decimal(10,3)")
.HasColumnName("value");
b.HasKey("Id");
b.HasIndex("IsSuperseded")
.HasFilter("is_superseded = true");
b.HasIndex("SourceBatchId");
b.HasIndex("EncounterId", "ObservationCode");
b.ToTable("live_observations", (string)null);
});
modelBuilder.Entity("Observation", b =>
{
b.Property<Guid>("Id")
@@ -1000,6 +1135,26 @@ namespace VigilCareRecordsAPI.Data.Migrations
b.Navigation("Patient");
});
modelBuilder.Entity("LiveEncounter", b =>
{
b.HasOne("Patient", null)
.WithMany()
.HasForeignKey("PatientId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("LiveObservation", b =>
{
b.HasOne("LiveEncounter", "Encounter")
.WithMany("Observations")
.HasForeignKey("EncounterId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Encounter");
});
modelBuilder.Entity("Observation", b =>
{
b.HasOne("Encounter", "Encounter")
@@ -1060,6 +1215,11 @@ namespace VigilCareRecordsAPI.Data.Migrations
b.Navigation("Events");
});
modelBuilder.Entity("LiveEncounter", b =>
{
b.Navigation("Observations");
});
#pragma warning restore 612, 618
}
}