feature: Clinical Data Model Expansion & Observation Vocabulary

This commit is contained in:
voltsrage
2026-06-18 15:00:42 +08:00
parent 3b4c5c524b
commit 7d630fbbd9
33 changed files with 2636 additions and 36 deletions
@@ -6,6 +6,9 @@ public class Encounter
public EncounterStatus Status { get; set; }
public Department Department { get; set; }
public string AttendingPhysician { get; set; } = null!;
public string? RoomBed { get; set; }
public string? AdmissionReason { get; set; }
public string? DischargeDiagnosis { get; set; }
public DateTimeOffset AdmittedAt { get; set; }
public DateTimeOffset? DischargedAt { get; set; }
public DateTimeOffset CreatedAt { get; set; }
@@ -6,6 +6,10 @@ public class Patient
public string LastName { get; set; } = null!;
public DateOnly DateOfBirth { get; set; }
public string Gender { get; set; } = null!;
public BloodType? BloodType { get; set; }
public string? Allergies { get; set; }
public string? EmergencyContactName { get; set; }
public string? EmergencyContactPhone { get; set; }
public string Status { get; set; } = "active";
public DateTimeOffset CreatedAt { get; set; }
@@ -6,7 +6,12 @@ public enum AlertType
CriticalPotassiumMeqL,
CriticalSpo2,
CriticalRespRate,
CriticalWbcKUl
CriticalWbcKUl,
CriticalSystolicBp,
CriticalDiastolicBp,
CriticalLactateMmolL,
CriticalAvpu,
CriticalGlucoseMgDl
}
public static class AlertTypeExtensions
@@ -20,6 +25,11 @@ public static class AlertTypeExtensions
AlertType.CriticalSpo2 => "CRITICAL_SPO2",
AlertType.CriticalRespRate => "CRITICAL_RESP_RATE",
AlertType.CriticalWbcKUl => "CRITICAL_WBC_K_UL",
AlertType.CriticalSystolicBp => "CRITICAL_SYSTOLIC_BP",
AlertType.CriticalDiastolicBp => "CRITICAL_DIASTOLIC_BP",
AlertType.CriticalLactateMmolL => "CRITICAL_LACTATE_MMOL_L",
AlertType.CriticalAvpu => "CRITICAL_AVPU",
AlertType.CriticalGlucoseMgDl => "CRITICAL_GLUCOSE_MG_DL",
_ => throw new ArgumentOutOfRangeException(nameof(t))
};
@@ -32,6 +42,11 @@ public static class AlertTypeExtensions
"CRITICAL_SPO2" => AlertType.CriticalSpo2,
"CRITICAL_RESP_RATE" => AlertType.CriticalRespRate,
"CRITICAL_WBC_K_UL" => AlertType.CriticalWbcKUl,
"CRITICAL_SYSTOLIC_BP" => AlertType.CriticalSystolicBp,
"CRITICAL_DIASTOLIC_BP" => AlertType.CriticalDiastolicBp,
"CRITICAL_LACTATE_MMOL_L" => AlertType.CriticalLactateMmolL,
"CRITICAL_AVPU" => AlertType.CriticalAvpu,
"CRITICAL_GLUCOSE_MG_DL" => AlertType.CriticalGlucoseMgDl,
_ => throw new ArgumentOutOfRangeException(nameof(v), $"Unknown alert type: '{v}'")
};
@@ -44,6 +59,11 @@ public static class AlertTypeExtensions
"SPO2" => AlertType.CriticalSpo2,
"RESP_RATE" => AlertType.CriticalRespRate,
"WBC_K_UL" => AlertType.CriticalWbcKUl,
"SYSTOLIC_BP" => AlertType.CriticalSystolicBp,
"DIASTOLIC_BP" => AlertType.CriticalDiastolicBp,
"LACTATE_MMOL_L" => AlertType.CriticalLactateMmolL,
"AVPU" => AlertType.CriticalAvpu,
"GLUCOSE_MG_DL" => AlertType.CriticalGlucoseMgDl,
_ => throw new ArgumentOutOfRangeException(
nameof(observationCode), $"No critical alert type for observation code '{observationCode}'")
};
@@ -0,0 +1,40 @@
public enum BloodType
{
APositive,
ANegative,
BPositive,
BNegative,
AbPositive,
AbNegative,
OPositive,
ONegative
}
public static class BloodTypeExtensions
{
public static string ToDbString(this BloodType b) => b switch
{
BloodType.APositive => "A+",
BloodType.ANegative => "A-",
BloodType.BPositive => "B+",
BloodType.BNegative => "B-",
BloodType.AbPositive => "AB+",
BloodType.AbNegative => "AB-",
BloodType.OPositive => "O+",
BloodType.ONegative => "O-",
_ => throw new ArgumentOutOfRangeException(nameof(b))
};
public static BloodType FromDbString(string v) => v switch
{
"A+" => BloodType.APositive,
"A-" => BloodType.ANegative,
"B+" => BloodType.BPositive,
"B-" => BloodType.BNegative,
"AB+" => BloodType.AbPositive,
"AB-" => BloodType.AbNegative,
"O+" => BloodType.OPositive,
"O-" => BloodType.ONegative,
_ => throw new ArgumentOutOfRangeException(nameof(v), $"Unknown blood type: '{v}'")
};
}
@@ -0,0 +1,44 @@
using System.Text.Json;
using System.Text.Json.Serialization;
public sealed class BloodTypeJsonConverter : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert) =>
typeToConvert == typeof(BloodType) || typeToConvert == typeof(BloodType?);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
if (typeToConvert == typeof(BloodType))
return new BloodTypeConverter();
return new NullableBloodTypeConverter();
}
private sealed class BloodTypeConverter : JsonConverter<BloodType>
{
public override BloodType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> BloodTypeExtensions.FromDbString(reader.GetString()!);
public override void Write(Utf8JsonWriter writer, BloodType value, JsonSerializerOptions options)
=> writer.WriteStringValue(value.ToDbString());
}
private sealed class NullableBloodTypeConverter : JsonConverter<BloodType?>
{
public override BloodType? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
return null;
return BloodTypeExtensions.FromDbString(reader.GetString()!);
}
public override void Write(Utf8JsonWriter writer, BloodType? value, JsonSerializerOptions options)
{
if (value is null)
writer.WriteNullValue();
else
writer.WriteStringValue(value.Value.ToDbString());
}
}
}