feat(health): dialysis/lab_report/diagnosis PII 加密
- 迁移 m000069-m000071: 三个表添加 key_version - dialysis_record: symptoms(JSON) + complication_notes 加密 - lab_report: items(JSON) + doctor_notes 加密 - diagnosis: notes 加密 - JSON 字段: serialize → encrypt → Value::String(ciphertext) - 解密失败时回退原始值(兼容未迁移明文数据)
This commit is contained in:
@@ -28,6 +28,8 @@ pub struct Model {
|
||||
#[sea_orm(skip_serializing_if = "Option::is_none")]
|
||||
pub deleted_at: Option<DateTimeUtc>,
|
||||
pub version: i32,
|
||||
#[sea_orm(skip_serializing_if = "Option::is_none")]
|
||||
pub key_version: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
||||
@@ -58,6 +58,8 @@ pub struct Model {
|
||||
#[sea_orm(skip_serializing_if = "Option::is_none")]
|
||||
pub deleted_at: Option<DateTimeUtc>,
|
||||
pub version: i32,
|
||||
#[sea_orm(skip_serializing_if = "Option::is_none")]
|
||||
pub key_version: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
||||
@@ -36,6 +36,8 @@ pub struct Model {
|
||||
#[sea_orm(skip_serializing_if = "Option::is_none")]
|
||||
pub deleted_at: Option<DateTimeUtc>,
|
||||
pub version: i32,
|
||||
#[sea_orm(skip_serializing_if = "Option::is_none")]
|
||||
pub key_version: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
||||
@@ -4,6 +4,7 @@ use uuid::Uuid;
|
||||
|
||||
use erp_core::audit::AuditLog;
|
||||
use erp_core::audit_service;
|
||||
use erp_core::crypto as pii;
|
||||
use erp_core::error::check_version;
|
||||
use erp_core::types::PaginatedResponse;
|
||||
|
||||
@@ -37,7 +38,8 @@ pub async fn list_diagnoses(
|
||||
.await?;
|
||||
|
||||
let total_pages = total.div_ceil(limit.max(1));
|
||||
let data = models.into_iter().map(to_resp).collect();
|
||||
let crypto = &state.crypto;
|
||||
let data = models.into_iter().map(|m| to_resp(crypto, m)).collect();
|
||||
|
||||
Ok(PaginatedResponse { data, total, page, page_size: limit, total_pages })
|
||||
}
|
||||
@@ -64,6 +66,12 @@ pub async fn create_diagnosis(
|
||||
return Err(HealthError::Validation("诊断状态无效,可选: active/resolved/chronic".into()));
|
||||
}
|
||||
|
||||
// PII 加密
|
||||
let kek = state.crypto.kek();
|
||||
let encrypted_notes = req.notes.as_ref()
|
||||
.map(|n| pii::encrypt(kek, n))
|
||||
.transpose()?;
|
||||
|
||||
let now = Utc::now();
|
||||
let active = diagnosis::ActiveModel {
|
||||
id: Set(Uuid::now_v7()),
|
||||
@@ -76,13 +84,14 @@ pub async fn create_diagnosis(
|
||||
diagnosed_date: Set(req.diagnosed_date),
|
||||
status: Set(req.status),
|
||||
diagnosed_by: Set(req.diagnosed_by.or(operator_id)),
|
||||
notes: Set(req.notes),
|
||||
notes: Set(encrypted_notes),
|
||||
created_at: Set(now),
|
||||
updated_at: Set(now),
|
||||
created_by: Set(operator_id),
|
||||
updated_by: Set(operator_id),
|
||||
deleted_at: Set(None),
|
||||
version: Set(1),
|
||||
key_version: Set(Some(1)),
|
||||
};
|
||||
let m = active.insert(&state.db).await?;
|
||||
|
||||
@@ -92,7 +101,7 @@ pub async fn create_diagnosis(
|
||||
&state.db,
|
||||
).await;
|
||||
|
||||
Ok(to_resp(m))
|
||||
Ok(to_resp(&state.crypto, m))
|
||||
}
|
||||
|
||||
pub async fn update_diagnosis(
|
||||
@@ -132,10 +141,15 @@ pub async fn update_diagnosis(
|
||||
}
|
||||
if let Some(v) = req.health_record_id { active.health_record_id = Set(Some(v)); }
|
||||
if let Some(v) = req.diagnosed_by { active.diagnosed_by = Set(Some(v)); }
|
||||
if let Some(v) = req.notes { active.notes = Set(Some(v)); }
|
||||
if let Some(v) = req.notes {
|
||||
let kek = state.crypto.kek();
|
||||
let encrypted = pii::encrypt(kek, &v)?;
|
||||
active.notes = Set(Some(encrypted));
|
||||
}
|
||||
active.updated_at = Set(Utc::now());
|
||||
active.updated_by = Set(operator_id);
|
||||
active.version = Set(next_ver);
|
||||
active.key_version = Set(Some(1));
|
||||
|
||||
let m = active.update(&state.db).await?;
|
||||
|
||||
@@ -145,7 +159,7 @@ pub async fn update_diagnosis(
|
||||
&state.db,
|
||||
).await;
|
||||
|
||||
Ok(to_resp(m))
|
||||
Ok(to_resp(&state.crypto, m))
|
||||
}
|
||||
|
||||
pub async fn delete_diagnosis(
|
||||
@@ -182,7 +196,14 @@ pub async fn delete_diagnosis(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn to_resp(m: diagnosis::Model) -> DiagnosisResp {
|
||||
fn to_resp(crypto: &erp_core::crypto::PiiCrypto, m: diagnosis::Model) -> DiagnosisResp {
|
||||
let kek = crypto.kek();
|
||||
|
||||
// 解密备注
|
||||
let notes = m.notes.as_ref()
|
||||
.map(|n| pii::decrypt(kek, n).unwrap_or_else(|_| n.clone()))
|
||||
.or(m.notes);
|
||||
|
||||
DiagnosisResp {
|
||||
id: m.id,
|
||||
patient_id: m.patient_id,
|
||||
@@ -193,7 +214,7 @@ fn to_resp(m: diagnosis::Model) -> DiagnosisResp {
|
||||
diagnosed_date: m.diagnosed_date,
|
||||
status: m.status,
|
||||
diagnosed_by: m.diagnosed_by,
|
||||
notes: m.notes,
|
||||
notes,
|
||||
created_at: m.created_at,
|
||||
updated_at: m.updated_at,
|
||||
version: m.version,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use chrono::Utc;
|
||||
use erp_core::audit::AuditLog;
|
||||
use erp_core::audit_service;
|
||||
use erp_core::crypto as pii;
|
||||
use num_traits::ToPrimitive;
|
||||
use sea_orm::entity::prelude::*;
|
||||
use sea_orm::{ActiveValue::Set, QueryOrder, QuerySelect};
|
||||
@@ -40,7 +41,8 @@ pub async fn list_dialysis_records(
|
||||
.await?;
|
||||
|
||||
let total_pages = total.div_ceil(limit.max(1));
|
||||
let data: Vec<DialysisRecordResp> = models.into_iter().map(to_resp).collect();
|
||||
let crypto = &state.crypto;
|
||||
let data: Vec<DialysisRecordResp> = models.into_iter().map(|m| to_resp(crypto, m)).collect();
|
||||
|
||||
Ok(PaginatedResponse { data, total, page, page_size: limit, total_pages })
|
||||
}
|
||||
@@ -58,7 +60,7 @@ pub async fn get_dialysis_record(
|
||||
.await?
|
||||
.ok_or(HealthError::DialysisRecordNotFound)?;
|
||||
|
||||
Ok(to_resp(m))
|
||||
Ok(to_resp(&state.crypto, m))
|
||||
}
|
||||
|
||||
pub async fn create_dialysis_record(
|
||||
@@ -77,6 +79,21 @@ pub async fn create_dialysis_record(
|
||||
|
||||
validate_dialysis_type(&req.dialysis_type)?;
|
||||
|
||||
let kek = state.crypto.kek();
|
||||
|
||||
// PII 加密
|
||||
let encrypted_symptoms = req.symptoms.as_ref()
|
||||
.map(|v| -> HealthResult<serde_json::Value> {
|
||||
let json_str = serde_json::to_string(v)
|
||||
.map_err(|e| HealthError::Validation(e.to_string()))?;
|
||||
Ok(serde_json::Value::String(pii::encrypt(kek, &json_str)?))
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let encrypted_complication = req.complication_notes.as_ref()
|
||||
.map(|c| pii::encrypt(kek, c))
|
||||
.transpose()?;
|
||||
|
||||
let now = Utc::now();
|
||||
let active = dialysis_record::ActiveModel {
|
||||
id: Set(Uuid::now_v7()),
|
||||
@@ -98,8 +115,8 @@ pub async fn create_dialysis_record(
|
||||
dialysis_duration: Set(req.dialysis_duration),
|
||||
blood_flow_rate: Set(req.blood_flow_rate),
|
||||
dialysis_type: Set(req.dialysis_type),
|
||||
symptoms: Set(req.symptoms),
|
||||
complication_notes: Set(req.complication_notes),
|
||||
symptoms: Set(encrypted_symptoms),
|
||||
complication_notes: Set(encrypted_complication),
|
||||
status: Set("draft".to_string()),
|
||||
reviewed_by: Set(None),
|
||||
reviewed_at: Set(None),
|
||||
@@ -109,6 +126,7 @@ pub async fn create_dialysis_record(
|
||||
updated_by: Set(operator_id),
|
||||
deleted_at: Set(None),
|
||||
version: Set(1),
|
||||
key_version: Set(Some(1)),
|
||||
};
|
||||
let m = active.insert(&state.db).await?;
|
||||
|
||||
@@ -118,7 +136,7 @@ pub async fn create_dialysis_record(
|
||||
&state.db,
|
||||
).await;
|
||||
|
||||
Ok(to_resp(m))
|
||||
Ok(to_resp(&state.crypto, m))
|
||||
}
|
||||
|
||||
pub async fn update_dialysis_record(
|
||||
@@ -157,11 +175,22 @@ pub async fn update_dialysis_record(
|
||||
if let Some(v) = req.dialysis_duration { active.dialysis_duration = Set(Some(v)); }
|
||||
if let Some(v) = req.blood_flow_rate { active.blood_flow_rate = Set(Some(v)); }
|
||||
if let Some(ref v) = req.dialysis_type { validate_dialysis_type(v)?; active.dialysis_type = Set(v.clone()); }
|
||||
if let Some(v) = req.symptoms { active.symptoms = Set(Some(v)); }
|
||||
if let Some(v) = req.complication_notes { active.complication_notes = Set(Some(v)); }
|
||||
if let Some(v) = req.symptoms {
|
||||
let kek = state.crypto.kek();
|
||||
let encrypted = Some(serde_json::Value::String(
|
||||
pii::encrypt(kek, &serde_json::to_string(&v).unwrap_or_default())?
|
||||
));
|
||||
active.symptoms = Set(encrypted);
|
||||
}
|
||||
if let Some(v) = req.complication_notes {
|
||||
let kek = state.crypto.kek();
|
||||
let encrypted = pii::encrypt(kek, &v)?;
|
||||
active.complication_notes = Set(Some(encrypted));
|
||||
}
|
||||
active.updated_at = Set(Utc::now());
|
||||
active.updated_by = Set(operator_id);
|
||||
active.version = Set(next_ver);
|
||||
active.key_version = Set(Some(1));
|
||||
|
||||
let m = active.update(&state.db).await?;
|
||||
|
||||
@@ -171,7 +200,7 @@ pub async fn update_dialysis_record(
|
||||
&state.db,
|
||||
).await;
|
||||
|
||||
Ok(to_resp(m))
|
||||
Ok(to_resp(&state.crypto, m))
|
||||
}
|
||||
|
||||
pub async fn review_dialysis_record(
|
||||
@@ -208,7 +237,7 @@ pub async fn review_dialysis_record(
|
||||
&state.db,
|
||||
).await;
|
||||
|
||||
Ok(to_resp(m))
|
||||
Ok(to_resp(&state.crypto, m))
|
||||
}
|
||||
|
||||
pub async fn delete_dialysis_record(
|
||||
@@ -254,7 +283,21 @@ fn validate_dialysis_type(dialysis_type: &str) -> HealthResult<()> {
|
||||
}
|
||||
}
|
||||
|
||||
fn to_resp(m: dialysis_record::Model) -> DialysisRecordResp {
|
||||
fn to_resp(crypto: &erp_core::crypto::PiiCrypto, m: dialysis_record::Model) -> DialysisRecordResp {
|
||||
let kek = crypto.kek();
|
||||
|
||||
// 解密症状 JSON(加密时存储为 Value::String(ciphertext))
|
||||
let symptoms = m.symptoms.as_ref()
|
||||
.and_then(|v| v.as_str())
|
||||
.and_then(|s| pii::decrypt(kek, s).ok())
|
||||
.and_then(|s| serde_json::from_str(&s).ok())
|
||||
.or(m.symptoms);
|
||||
|
||||
// 解密并发症备注
|
||||
let complication_notes = m.complication_notes.as_ref()
|
||||
.map(|c| pii::decrypt(kek, c).unwrap_or_else(|_| c.clone()))
|
||||
.or(m.complication_notes);
|
||||
|
||||
DialysisRecordResp {
|
||||
id: m.id,
|
||||
patient_id: m.patient_id,
|
||||
@@ -274,8 +317,8 @@ fn to_resp(m: dialysis_record::Model) -> DialysisRecordResp {
|
||||
dialysis_duration: m.dialysis_duration,
|
||||
blood_flow_rate: m.blood_flow_rate,
|
||||
dialysis_type: m.dialysis_type,
|
||||
symptoms: m.symptoms,
|
||||
complication_notes: m.complication_notes,
|
||||
symptoms,
|
||||
complication_notes,
|
||||
status: m.status,
|
||||
reviewed_by: m.reviewed_by,
|
||||
reviewed_at: m.reviewed_at,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use chrono::Utc;
|
||||
use erp_core::audit::AuditLog;
|
||||
use erp_core::audit_service;
|
||||
use erp_core::crypto as pii;
|
||||
use erp_core::events::DomainEvent;
|
||||
use num_traits::ToPrimitive;
|
||||
use sea_orm::entity::prelude::*;
|
||||
@@ -271,12 +272,27 @@ pub async fn list_lab_reports(
|
||||
.await?;
|
||||
|
||||
let total_pages = total.div_ceil(limit.max(1));
|
||||
let data = models.into_iter().map(|m| LabReportResp {
|
||||
id: m.id, patient_id: m.patient_id, report_date: m.report_date,
|
||||
report_type: m.report_type, source: m.source,
|
||||
items: m.items, image_urls: m.image_urls, doctor_notes: m.doctor_notes,
|
||||
status: m.status, reviewed_by: m.reviewed_by, reviewed_at: m.reviewed_at,
|
||||
created_at: m.created_at, updated_at: m.updated_at, version: m.version,
|
||||
let kek = state.crypto.kek();
|
||||
let data = models.into_iter().map(|m| {
|
||||
// 解密 items JSON(加密时存储为 Value::String(ciphertext))
|
||||
let items = m.items.as_ref()
|
||||
.and_then(|v| v.as_str())
|
||||
.and_then(|s| pii::decrypt(kek, s).ok())
|
||||
.and_then(|s| serde_json::from_str(&s).ok())
|
||||
.or(m.items.clone());
|
||||
|
||||
// 解密医生备注
|
||||
let doctor_notes = m.doctor_notes.as_ref()
|
||||
.map(|c| pii::decrypt(kek, c).unwrap_or_else(|_| c.clone()))
|
||||
.or(m.doctor_notes.clone());
|
||||
|
||||
LabReportResp {
|
||||
id: m.id, patient_id: m.patient_id, report_date: m.report_date,
|
||||
report_type: m.report_type, source: m.source,
|
||||
items, image_urls: m.image_urls, doctor_notes,
|
||||
status: m.status, reviewed_by: m.reviewed_by, reviewed_at: m.reviewed_at,
|
||||
created_at: m.created_at, updated_at: m.updated_at, version: m.version,
|
||||
}
|
||||
}).collect();
|
||||
|
||||
Ok(PaginatedResponse { data, total, page, page_size: limit, total_pages })
|
||||
@@ -298,6 +314,21 @@ pub async fn create_lab_report(
|
||||
.await?
|
||||
.ok_or(HealthError::PatientNotFound)?;
|
||||
|
||||
let kek = state.crypto.kek();
|
||||
|
||||
// PII 加密
|
||||
let encrypted_items = req.items.as_ref()
|
||||
.map(|v| -> HealthResult<serde_json::Value> {
|
||||
let json_str = serde_json::to_string(v)
|
||||
.map_err(|e| HealthError::Validation(e.to_string()))?;
|
||||
Ok(serde_json::Value::String(pii::encrypt(kek, &json_str)?))
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let encrypted_doctor_notes = req.doctor_notes.as_ref()
|
||||
.map(|c| pii::encrypt(kek, c))
|
||||
.transpose()?;
|
||||
|
||||
let now = Utc::now();
|
||||
let active = lab_report::ActiveModel {
|
||||
id: Set(Uuid::now_v7()),
|
||||
@@ -306,9 +337,9 @@ pub async fn create_lab_report(
|
||||
report_date: Set(req.report_date),
|
||||
report_type: Set(req.report_type),
|
||||
source: Set(req.source),
|
||||
items: Set(req.items),
|
||||
items: Set(encrypted_items),
|
||||
image_urls: Set(req.image_urls),
|
||||
doctor_notes: Set(req.doctor_notes),
|
||||
doctor_notes: Set(encrypted_doctor_notes),
|
||||
status: Set("pending".to_string()),
|
||||
reviewed_by: Set(None),
|
||||
reviewed_at: Set(None),
|
||||
@@ -318,6 +349,7 @@ pub async fn create_lab_report(
|
||||
updated_by: Set(operator_id),
|
||||
deleted_at: Set(None),
|
||||
version: Set(1),
|
||||
key_version: Set(Some(1)),
|
||||
};
|
||||
let m = active.insert(&state.db).await?;
|
||||
|
||||
@@ -334,10 +366,21 @@ pub async fn create_lab_report(
|
||||
&state.db,
|
||||
).await;
|
||||
|
||||
// 解密返回
|
||||
let decrypted_items = m.items.as_ref()
|
||||
.and_then(|v| v.as_str())
|
||||
.and_then(|s| pii::decrypt(kek, s).ok())
|
||||
.and_then(|s| serde_json::from_str(&s).ok())
|
||||
.or(m.items);
|
||||
|
||||
let decrypted_doctor_notes = m.doctor_notes.as_ref()
|
||||
.map(|c| pii::decrypt(kek, c).unwrap_or_else(|_| c.clone()))
|
||||
.or(m.doctor_notes);
|
||||
|
||||
Ok(LabReportResp {
|
||||
id: m.id, patient_id: m.patient_id, report_date: m.report_date,
|
||||
report_type: m.report_type, source: m.source,
|
||||
items: m.items, image_urls: m.image_urls, doctor_notes: m.doctor_notes,
|
||||
items: decrypted_items, image_urls: m.image_urls, doctor_notes: decrypted_doctor_notes,
|
||||
status: m.status, reviewed_by: m.reviewed_by, reviewed_at: m.reviewed_at,
|
||||
created_at: m.created_at, updated_at: m.updated_at, version: m.version,
|
||||
})
|
||||
@@ -367,12 +410,23 @@ pub async fn update_lab_report(
|
||||
if let Some(v) = req.report_date { active.report_date = Set(v); }
|
||||
if let Some(v) = req.report_type { active.report_type = Set(v); }
|
||||
if let Some(v) = req.source { active.source = Set(Some(v)); }
|
||||
if let Some(v) = req.items { active.items = Set(Some(v)); }
|
||||
if let Some(v) = req.items {
|
||||
let kek = state.crypto.kek();
|
||||
let encrypted = Some(serde_json::Value::String(
|
||||
pii::encrypt(kek, &serde_json::to_string(&v).unwrap_or_default())?
|
||||
));
|
||||
active.items = Set(encrypted);
|
||||
}
|
||||
if let Some(v) = req.image_urls { active.image_urls = Set(Some(v)); }
|
||||
if let Some(v) = req.doctor_notes { active.doctor_notes = Set(Some(v)); }
|
||||
if let Some(v) = req.doctor_notes {
|
||||
let kek = state.crypto.kek();
|
||||
let encrypted = pii::encrypt(kek, &v)?;
|
||||
active.doctor_notes = Set(Some(encrypted));
|
||||
}
|
||||
active.updated_at = Set(Utc::now());
|
||||
active.updated_by = Set(operator_id);
|
||||
active.version = Set(next_ver);
|
||||
active.key_version = Set(Some(1));
|
||||
|
||||
let m = active.update(&state.db).await?;
|
||||
|
||||
@@ -382,10 +436,22 @@ pub async fn update_lab_report(
|
||||
&state.db,
|
||||
).await;
|
||||
|
||||
// 解密返回
|
||||
let kek = state.crypto.kek();
|
||||
let decrypted_items = m.items.as_ref()
|
||||
.and_then(|v| v.as_str())
|
||||
.and_then(|s| pii::decrypt(kek, s).ok())
|
||||
.and_then(|s| serde_json::from_str(&s).ok())
|
||||
.or(m.items);
|
||||
|
||||
let decrypted_doctor_notes = m.doctor_notes.as_ref()
|
||||
.map(|c| pii::decrypt(kek, c).unwrap_or_else(|_| c.clone()))
|
||||
.or(m.doctor_notes);
|
||||
|
||||
Ok(LabReportResp {
|
||||
id: m.id, patient_id: m.patient_id, report_date: m.report_date,
|
||||
report_type: m.report_type, source: m.source,
|
||||
items: m.items, image_urls: m.image_urls, doctor_notes: m.doctor_notes,
|
||||
items: decrypted_items, image_urls: m.image_urls, doctor_notes: decrypted_doctor_notes,
|
||||
status: m.status, reviewed_by: m.reviewed_by, reviewed_at: m.reviewed_at,
|
||||
created_at: m.created_at, updated_at: m.updated_at, version: m.version,
|
||||
})
|
||||
@@ -450,11 +516,22 @@ pub async fn review_lab_report(
|
||||
active.status = Set("reviewed".to_string());
|
||||
active.reviewed_by = Set(Some(reviewer_id));
|
||||
active.reviewed_at = Set(Some(Utc::now()));
|
||||
if let Some(v) = req.doctor_notes { active.doctor_notes = Set(Some(v)); }
|
||||
if let Some(v) = req.items { active.items = Set(Some(v)); }
|
||||
if let Some(v) = req.doctor_notes {
|
||||
let kek = state.crypto.kek();
|
||||
let encrypted = pii::encrypt(kek, &v)?;
|
||||
active.doctor_notes = Set(Some(encrypted));
|
||||
}
|
||||
if let Some(v) = req.items {
|
||||
let kek = state.crypto.kek();
|
||||
let encrypted = Some(serde_json::Value::String(
|
||||
pii::encrypt(kek, &serde_json::to_string(&v).unwrap_or_default())?
|
||||
));
|
||||
active.items = Set(encrypted);
|
||||
}
|
||||
active.updated_at = Set(Utc::now());
|
||||
active.updated_by = Set(Some(reviewer_id));
|
||||
active.version = Set(next_ver);
|
||||
active.key_version = Set(Some(1));
|
||||
|
||||
let m = active.update(&state.db).await?;
|
||||
|
||||
@@ -464,10 +541,22 @@ pub async fn review_lab_report(
|
||||
&state.db,
|
||||
).await;
|
||||
|
||||
// 解密返回
|
||||
let kek = state.crypto.kek();
|
||||
let decrypted_items = m.items.as_ref()
|
||||
.and_then(|v| v.as_str())
|
||||
.and_then(|s| pii::decrypt(kek, s).ok())
|
||||
.and_then(|s| serde_json::from_str(&s).ok())
|
||||
.or(m.items);
|
||||
|
||||
let decrypted_doctor_notes = m.doctor_notes.as_ref()
|
||||
.map(|c| pii::decrypt(kek, c).unwrap_or_else(|_| c.clone()))
|
||||
.or(m.doctor_notes);
|
||||
|
||||
Ok(LabReportResp {
|
||||
id: m.id, patient_id: m.patient_id, report_date: m.report_date,
|
||||
report_type: m.report_type, source: m.source,
|
||||
items: m.items, image_urls: m.image_urls, doctor_notes: m.doctor_notes,
|
||||
items: decrypted_items, image_urls: m.image_urls, doctor_notes: decrypted_doctor_notes,
|
||||
status: m.status, reviewed_by: m.reviewed_by, reviewed_at: m.reviewed_at,
|
||||
created_at: m.created_at, updated_at: m.updated_at, version: m.version,
|
||||
})
|
||||
|
||||
@@ -68,6 +68,9 @@ mod m20260427_000065_add_consultation_message_key_version;
|
||||
mod m20260427_000066_add_follow_up_record_key_version;
|
||||
mod m20260427_000067_add_family_member_pii_fields;
|
||||
mod m20260427_000068_add_doctor_profile_pii_fields;
|
||||
mod m20260427_000069_add_dialysis_record_key_version;
|
||||
mod m20260427_000070_add_lab_report_key_version;
|
||||
mod m20260427_000071_add_diagnosis_key_version;
|
||||
|
||||
pub struct Migrator;
|
||||
|
||||
@@ -143,6 +146,9 @@ impl MigratorTrait for Migrator {
|
||||
Box::new(m20260427_000066_add_follow_up_record_key_version::Migration),
|
||||
Box::new(m20260427_000067_add_family_member_pii_fields::Migration),
|
||||
Box::new(m20260427_000068_add_doctor_profile_pii_fields::Migration),
|
||||
Box::new(m20260427_000069_add_dialysis_record_key_version::Migration),
|
||||
Box::new(m20260427_000070_add_lab_report_key_version::Migration),
|
||||
Box::new(m20260427_000071_add_diagnosis_key_version::Migration),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(DialysisRecord::Table)
|
||||
.add_column(ColumnDef::new(DialysisRecord::KeyVersion).integer().null())
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(DialysisRecord::Table)
|
||||
.drop_column(DialysisRecord::KeyVersion)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum DialysisRecord {
|
||||
Table,
|
||||
KeyVersion,
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(LabReport::Table)
|
||||
.add_column(ColumnDef::new(LabReport::KeyVersion).integer().null())
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(LabReport::Table)
|
||||
.drop_column(LabReport::KeyVersion)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum LabReport {
|
||||
Table,
|
||||
KeyVersion,
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(Diagnosis::Table)
|
||||
.add_column(ColumnDef::new(Diagnosis::KeyVersion).integer().null())
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(Diagnosis::Table)
|
||||
.drop_column(Diagnosis::KeyVersion)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum Diagnosis {
|
||||
Table,
|
||||
KeyVersion,
|
||||
}
|
||||
Reference in New Issue
Block a user