feat(health): V2 血透专科数据模型 — dialysis_record + lab_report 审阅流程
- 新增 dialysis_record 表和完整 CRUD API(透析日期/体重/血压/超滤量/透析类型/症状)
- ALTER lab_report 增加 source/status/reviewed_by/reviewed_at 字段
- 重命名 lab_report: indicators→items, doctor_interpretation→doctor_notes
- 新增透析记录审阅端点 PUT /dialysis-records/{id}/review
- 新增化验报告审阅端点 PUT /patients/{id}/lab-reports/{rid}/review
- 化验报告 items JSON 支持 V2 结构(name/value/unit/reference/is_abnormal)
- 迁移 m000051 含完整 up/down 回滚
- 94 个后端测试全部通过,API 全链路验证通过
This commit is contained in:
@@ -50,6 +50,7 @@ mod m20260424_000047_health_index_fix;
|
||||
mod m20260425_000048_add_patient_id_number_hash;
|
||||
mod m20260425_000049_widen_patient_id_number;
|
||||
mod m20260425_00050_add_doctor_name_column;
|
||||
mod m20260425_000051_dialysis_and_lab_enhance;
|
||||
|
||||
pub struct Migrator;
|
||||
|
||||
@@ -107,6 +108,7 @@ impl MigratorTrait for Migrator {
|
||||
Box::new(m20260425_000048_add_patient_id_number_hash::Migration),
|
||||
Box::new(m20260425_000049_widen_patient_id_number::Migration),
|
||||
Box::new(m20260425_00050_add_doctor_name_column::Migration),
|
||||
Box::new(m20260425_000051_dialysis_and_lab_enhance::Migration),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
/// V2 血透专科数据模型: dialysis_record 新表 + lab_report 增强审阅流程
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
// 1. 创建 dialysis_record 表
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(Alias::new("dialysis_record"))
|
||||
.if_not_exists()
|
||||
.col(ColumnDef::new(Alias::new("id")).uuid().not_null().primary_key())
|
||||
.col(ColumnDef::new(Alias::new("tenant_id")).uuid().not_null())
|
||||
.col(ColumnDef::new(Alias::new("patient_id")).uuid().not_null())
|
||||
.col(ColumnDef::new(Alias::new("dialysis_date")).date().not_null())
|
||||
.col(ColumnDef::new(Alias::new("start_time")).time())
|
||||
.col(ColumnDef::new(Alias::new("end_time")).time())
|
||||
// 体重 (Decimal 5,1)
|
||||
.col(ColumnDef::new(Alias::new("dry_weight")).decimal().extra("CHECK(dry_weight IS NULL OR dry_weight >= 0)"))
|
||||
.col(ColumnDef::new(Alias::new("pre_weight")).decimal().extra("CHECK(pre_weight IS NULL OR pre_weight >= 0)"))
|
||||
.col(ColumnDef::new(Alias::new("post_weight")).decimal().extra("CHECK(post_weight IS NULL OR post_weight >= 0)"))
|
||||
// 血压
|
||||
.col(ColumnDef::new(Alias::new("pre_bp_systolic")).integer())
|
||||
.col(ColumnDef::new(Alias::new("pre_bp_diastolic")).integer())
|
||||
.col(ColumnDef::new(Alias::new("post_bp_systolic")).integer())
|
||||
.col(ColumnDef::new(Alias::new("post_bp_diastolic")).integer())
|
||||
// 心率
|
||||
.col(ColumnDef::new(Alias::new("pre_heart_rate")).integer())
|
||||
.col(ColumnDef::new(Alias::new("post_heart_rate")).integer())
|
||||
// 透析参数
|
||||
.col(ColumnDef::new(Alias::new("ultrafiltration_volume")).integer())
|
||||
.col(ColumnDef::new(Alias::new("dialysis_duration")).integer())
|
||||
.col(ColumnDef::new(Alias::new("blood_flow_rate")).integer())
|
||||
// HD / HDF / HF
|
||||
.col(ColumnDef::new(Alias::new("dialysis_type")).string().not_null().default("HD"))
|
||||
.col(ColumnDef::new(Alias::new("symptoms")).json())
|
||||
.col(ColumnDef::new(Alias::new("complication_notes")).text())
|
||||
// draft / completed / reviewed
|
||||
.col(ColumnDef::new(Alias::new("status")).string().not_null().default("draft"))
|
||||
.col(ColumnDef::new(Alias::new("reviewed_by")).uuid())
|
||||
.col(ColumnDef::new(Alias::new("reviewed_at")).timestamp_with_time_zone())
|
||||
// 标准字段
|
||||
.col(ColumnDef::new(Alias::new("created_at")).timestamp_with_time_zone().not_null().default(Expr::current_timestamp()))
|
||||
.col(ColumnDef::new(Alias::new("updated_at")).timestamp_with_time_zone().not_null().default(Expr::current_timestamp()))
|
||||
.col(ColumnDef::new(Alias::new("created_by")).uuid())
|
||||
.col(ColumnDef::new(Alias::new("updated_by")).uuid())
|
||||
.col(ColumnDef::new(Alias::new("deleted_at")).timestamp_with_time_zone())
|
||||
.col(ColumnDef::new(Alias::new("version")).integer().not_null().default(1))
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// dialysis_record 索引
|
||||
manager
|
||||
.create_index(
|
||||
Index::create()
|
||||
.if_not_exists()
|
||||
.name("idx_dialysis_record_tenant_patient")
|
||||
.table(Alias::new("dialysis_record"))
|
||||
.col(Alias::new("tenant_id"))
|
||||
.col(Alias::new("patient_id"))
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_index(
|
||||
Index::create()
|
||||
.if_not_exists()
|
||||
.name("idx_dialysis_record_date")
|
||||
.table(Alias::new("dialysis_record"))
|
||||
.col(Alias::new("dialysis_date"))
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// 2. ALTER lab_report: 增加审阅流程字段
|
||||
// source: manual_input / photo_upload
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(Alias::new("lab_report"))
|
||||
.add_column(ColumnDef::new(Alias::new("source")).string().default("manual_input"))
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// status: pending / reviewed
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(Alias::new("lab_report"))
|
||||
.add_column(ColumnDef::new(Alias::new("status")).string().not_null().default("pending"))
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(Alias::new("lab_report"))
|
||||
.add_column(ColumnDef::new(Alias::new("reviewed_by")).uuid())
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(Alias::new("lab_report"))
|
||||
.add_column(ColumnDef::new(Alias::new("reviewed_at")).timestamp_with_time_zone())
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// 重命名 indicators → items (V2 JSON 结构含 name/value/unit/reference_low/reference_high/is_abnormal)
|
||||
manager.get_connection().execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
"ALTER TABLE lab_report RENAME COLUMN indicators TO items".to_string(),
|
||||
)).await?;
|
||||
|
||||
// 重命名 doctor_interpretation → doctor_notes
|
||||
manager.get_connection().execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
"ALTER TABLE lab_report RENAME COLUMN doctor_interpretation TO doctor_notes".to_string(),
|
||||
)).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
// 恢复 lab_report 列名
|
||||
manager.get_connection().execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
"ALTER TABLE lab_report RENAME COLUMN doctor_notes TO doctor_interpretation".to_string(),
|
||||
)).await?;
|
||||
|
||||
manager.get_connection().execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
"ALTER TABLE lab_report RENAME COLUMN items TO indicators".to_string(),
|
||||
)).await?;
|
||||
|
||||
// 删除新增列
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(Alias::new("lab_report"))
|
||||
.drop_column(Alias::new("reviewed_at"))
|
||||
.drop_column(Alias::new("reviewed_by"))
|
||||
.drop_column(Alias::new("status"))
|
||||
.drop_column(Alias::new("source"))
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// 删除 dialysis_record 表
|
||||
manager
|
||||
.drop_table(Table::drop().table(Alias::new("dialysis_record")).if_exists().to_owned())
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user