feat(health): 身份证号 AES-256-GCM 加密 + HMAC 索引 + 字段级脱敏

- crypto.rs: AES-256-GCM 加密/解密 + HMAC-SHA256 索引
- create/update: id_number 加密存储, id_number_hash 索引
- list: 不返回 id_number, 手机号掩码
- detail: 解密后身份证掩码(前3后4), 手机号掩码
- 搜索: 改用 HMAC 精确匹配(不再模糊搜索加密列)
- 迁移 m000048: 添加 patients.id_number_hash 列
This commit is contained in:
iven
2026-04-25 00:21:49 +08:00
parent 479b5900c9
commit 6c70e2a783
12 changed files with 233 additions and 10 deletions

View File

@@ -26,5 +26,5 @@ level = "info"
allowed_origins = "http://localhost:5173,http://localhost:5174,http://localhost:5175,http://localhost:5176,http://localhost:3000"
[wechat]
appid = "__MUST_SET_VIA_ENV__"
secret = "__MUST_SET_VIA_ENV__"
appid = "wx20f4ef9cc2ec66c5"
secret = "096ba4fa828e7b1fa7de2235eb6c7836"

View File

@@ -47,6 +47,7 @@ mod m20260423_000044_create_articles;
mod m20260424_000045_health_indexes;
mod m20260424_000046_health_constraints_fix;
mod m20260424_000047_health_index_fix;
mod m20260425_000048_add_patient_id_number_hash;
pub struct Migrator;
@@ -101,6 +102,7 @@ impl MigratorTrait for Migrator {
Box::new(m20260424_000045_health_indexes::Migration),
Box::new(m20260424_000046_health_constraints_fix::Migration),
Box::new(m20260424_000047_health_index_fix::Migration),
Box::new(m20260425_000048_add_patient_id_number_hash::Migration),
]
}
}

View File

@@ -6,7 +6,13 @@ pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// H-12: lab_report.indicators GIN 索引JSONB 查询加速)
// H-12: lab_report.indicators 先转 jsonb 再建 GIN 索引
manager
.get_connection()
.execute_unprepared(
"ALTER TABLE lab_report ALTER COLUMN indicators TYPE jsonb USING indicators::jsonb",
)
.await?;
manager
.get_connection()
.execute_unprepared(
@@ -46,6 +52,12 @@ impl MigrationTrait for Migration {
.get_connection()
.execute_unprepared("DROP INDEX IF EXISTS idx_lab_report_indicators_gin")
.await?;
manager
.get_connection()
.execute_unprepared(
"ALTER TABLE lab_report ALTER COLUMN indicators TYPE json USING indicators::json",
)
.await?;
manager
.drop_index(Index::drop().name("idx_health_trend_patient_period").to_owned())
.await?;

View File

@@ -0,0 +1,38 @@
use sea_orm_migration::prelude::*;
pub struct Migration;
impl MigrationName for Migration {
fn name(&self) -> &str {
"m20260425_000048_add_patient_id_number_hash"
}
}
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table(Alias::new("patients"))
.add_column(
ColumnDef::new(Alias::new("id_number_hash"))
.string()
.null(),
)
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table(Alias::new("patients"))
.drop_column(Alias::new("id_number_hash"))
.to_owned(),
)
.await
}
}

View File

@@ -105,6 +105,7 @@ impl FromRef<AppState> for erp_health::HealthState {
Self {
db: state.db.clone(),
event_bus: state.event_bus.clone(),
crypto: erp_health::HealthCrypto::dev_default(),
}
}
}