feat(health): 体征增加体温/SpO2/血糖类型字段
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

- 迁移 079: vital_signs 表新增 body_temperature/spo2/blood_sugar_type 列
- Entity/DTO/Service 全链路支持新字段
- blood_sugar_type: fasting/postprandial/random/ogtt
- daily_monitoring 兼容层补全新字段为 None
This commit is contained in:
iven
2026-04-27 11:31:40 +08:00
parent 7e66561a5f
commit 67f2d07809
6 changed files with 96 additions and 0 deletions

View File

@@ -20,6 +20,10 @@ pub struct CreateVitalSignsReq {
pub heart_rate: Option<i32>,
pub weight: Option<Decimal>,
pub blood_sugar: Option<Decimal>,
pub body_temperature: Option<Decimal>,
pub spo2: Option<i32>,
/// fasting / postprandial / random / ogtt
pub blood_sugar_type: Option<String>,
pub water_intake_ml: Option<i32>,
pub urine_output_ml: Option<i32>,
pub notes: Option<String>,
@@ -42,6 +46,9 @@ pub struct UpdateVitalSignsReq {
pub heart_rate: Option<i32>,
pub weight: Option<Decimal>,
pub blood_sugar: Option<Decimal>,
pub body_temperature: Option<Decimal>,
pub spo2: Option<i32>,
pub blood_sugar_type: Option<String>,
pub water_intake_ml: Option<i32>,
pub urine_output_ml: Option<i32>,
pub notes: Option<String>,
@@ -66,6 +73,9 @@ pub struct VitalSignsResp {
pub heart_rate: Option<i32>,
pub weight: Option<Decimal>,
pub blood_sugar: Option<Decimal>,
pub body_temperature: Option<Decimal>,
pub spo2: Option<i32>,
pub blood_sugar_type: Option<String>,
pub water_intake_ml: Option<i32>,
pub urine_output_ml: Option<i32>,
pub notes: Option<String>,

View File

@@ -24,6 +24,12 @@ pub struct Model {
#[sea_orm(skip_serializing_if = "Option::is_none")]
pub blood_sugar: Option<Decimal>,
#[sea_orm(skip_serializing_if = "Option::is_none")]
pub body_temperature: Option<Decimal>,
#[sea_orm(skip_serializing_if = "Option::is_none")]
pub spo2: Option<i32>,
#[sea_orm(skip_serializing_if = "Option::is_none")]
pub blood_sugar_type: Option<String>,
#[sea_orm(skip_serializing_if = "Option::is_none")]
pub water_intake_ml: Option<i32>,
#[sea_orm(skip_serializing_if = "Option::is_none")]
pub urine_output_ml: Option<i32>,

View File

@@ -83,6 +83,9 @@ pub async fn create_daily_monitoring(
heart_rate: None,
weight: req.weight,
blood_sugar: req.blood_sugar,
body_temperature: None,
spo2: None,
blood_sugar_type: None,
water_intake_ml: req.fluid_intake,
urine_output_ml: req.urine_output,
notes: req.notes,
@@ -121,6 +124,9 @@ pub async fn update_daily_monitoring(
heart_rate: None,
weight: req.weight,
blood_sugar: req.blood_sugar,
body_temperature: None,
spo2: None,
blood_sugar_type: None,
water_intake_ml: req.fluid_intake,
urine_output_ml: req.urine_output,
notes: req.notes,

View File

@@ -59,6 +59,9 @@ pub async fn list_vital_signs(
heart_rate: m.heart_rate,
weight: m.weight.map(|d| d.to_f64().unwrap_or(0.0)),
blood_sugar: m.blood_sugar.map(|d| d.to_f64().unwrap_or(0.0)),
body_temperature: m.body_temperature.map(|d| d.to_f64().unwrap_or(0.0)),
spo2: m.spo2,
blood_sugar_type: m.blood_sugar_type,
water_intake_ml: m.water_intake_ml,
urine_output_ml: m.urine_output_ml,
notes: m.notes,
@@ -100,6 +103,9 @@ pub async fn create_vital_signs(
heart_rate: Set(req.heart_rate),
weight: Set(req.weight.map(|v| sea_orm::prelude::Decimal::from_f64_retain(v).unwrap_or_default())),
blood_sugar: Set(req.blood_sugar.map(|v| sea_orm::prelude::Decimal::from_f64_retain(v).unwrap_or_default())),
body_temperature: Set(req.body_temperature.map(|v| sea_orm::prelude::Decimal::from_f64_retain(v).unwrap_or_default())),
spo2: Set(req.spo2),
blood_sugar_type: Set(req.blood_sugar_type),
water_intake_ml: Set(req.water_intake_ml),
urine_output_ml: Set(req.urine_output_ml),
notes: Set(req.notes),
@@ -131,6 +137,9 @@ pub async fn create_vital_signs(
heart_rate: m.heart_rate,
weight: m.weight.map(|d| d.to_f64().unwrap_or(0.0)),
blood_sugar: m.blood_sugar.map(|d| d.to_f64().unwrap_or(0.0)),
body_temperature: m.body_temperature.map(|d| d.to_f64().unwrap_or(0.0)),
spo2: m.spo2,
blood_sugar_type: m.blood_sugar_type,
water_intake_ml: m.water_intake_ml, urine_output_ml: m.urine_output_ml,
notes: m.notes, created_at: m.created_at, updated_at: m.updated_at, version: m.version,
})
@@ -178,6 +187,9 @@ pub async fn update_vital_signs(
if let Some(v) = req.heart_rate { active.heart_rate = Set(Some(v)); }
if let Some(v) = req.weight { active.weight = Set(Some(sea_orm::prelude::Decimal::from_f64_retain(v).unwrap_or_default())); }
if let Some(v) = req.blood_sugar { active.blood_sugar = Set(Some(sea_orm::prelude::Decimal::from_f64_retain(v).unwrap_or_default())); }
if let Some(v) = req.body_temperature { active.body_temperature = Set(Some(sea_orm::prelude::Decimal::from_f64_retain(v).unwrap_or_default())); }
if let Some(v) = req.spo2 { active.spo2 = Set(Some(v)); }
if let Some(v) = req.blood_sugar_type { active.blood_sugar_type = Set(Some(v)); }
if let Some(v) = req.water_intake_ml { active.water_intake_ml = Set(Some(v)); }
if let Some(v) = req.urine_output_ml { active.urine_output_ml = Set(Some(v)); }
if let Some(v) = req.notes { active.notes = Set(Some(v)); }
@@ -210,6 +222,9 @@ pub async fn update_vital_signs(
heart_rate: m.heart_rate,
weight: m.weight.map(|d| d.to_f64().unwrap_or(0.0)),
blood_sugar: m.blood_sugar.map(|d| d.to_f64().unwrap_or(0.0)),
body_temperature: m.body_temperature.map(|d| d.to_f64().unwrap_or(0.0)),
spo2: m.spo2,
blood_sugar_type: m.blood_sugar_type.clone(),
water_intake_ml: m.water_intake_ml,
urine_output_ml: m.urine_output_ml,
notes: m.notes.clone(),
@@ -232,6 +247,9 @@ pub async fn update_vital_signs(
heart_rate: m.heart_rate,
weight: m.weight.map(|d| d.to_f64().unwrap_or(0.0)),
blood_sugar: m.blood_sugar.map(|d| d.to_f64().unwrap_or(0.0)),
body_temperature: m.body_temperature.map(|d| d.to_f64().unwrap_or(0.0)),
spo2: m.spo2,
blood_sugar_type: m.blood_sugar_type,
water_intake_ml: m.water_intake_ml, urine_output_ml: m.urine_output_ml,
notes: m.notes, created_at: m.created_at, updated_at: m.updated_at, version: m.version,
})

View File

@@ -78,6 +78,7 @@ mod m20260426_000075_create_patient_devices;
mod m20260426_000076_create_alert_rules;
mod m20260426_000077_create_alerts;
mod m20260427_000078_normalize_follow_up_types;
mod m20260427_000079_add_vital_signs_fields;
pub struct Migrator;
@@ -163,6 +164,7 @@ impl MigratorTrait for Migrator {
Box::new(m20260426_000076_create_alert_rules::Migration),
Box::new(m20260426_000077_create_alerts::Migration),
Box::new(m20260427_000078_normalize_follow_up_types::Migration),
Box::new(m20260427_000079_add_vital_signs_fields::Migration),
]
}
}

View File

@@ -0,0 +1,54 @@
use sea_orm_migration::prelude::*;
pub struct Migration;
impl MigrationName for Migration {
fn name(&self) -> &str {
"m20260427_000079_add_vital_signs_fields"
}
}
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let conn = manager.get_connection();
conn.execute_unprepared(
"ALTER TABLE vital_signs ADD COLUMN IF NOT EXISTS body_temperature DECIMAL(4,1)",
)
.await?;
conn.execute_unprepared(
"ALTER TABLE vital_signs ADD COLUMN IF NOT EXISTS spo2 INTEGER",
)
.await?;
conn.execute_unprepared(
"ALTER TABLE vital_signs ADD COLUMN IF NOT EXISTS blood_sugar_type VARCHAR(20) DEFAULT 'fasting'",
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let conn = manager.get_connection();
conn.execute_unprepared(
"ALTER TABLE vital_signs DROP COLUMN IF EXISTS blood_sugar_type",
)
.await?;
conn.execute_unprepared(
"ALTER TABLE vital_signs DROP COLUMN IF EXISTS spo2",
)
.await?;
conn.execute_unprepared(
"ALTER TABLE vital_signs DROP COLUMN IF EXISTS body_temperature",
)
.await?;
Ok(())
}
}