fix(health): 数据完整性 + 代码规范修复 — FK约束/版本类型统一/软删除过滤
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

数据完整性:
- 新增 8 个 FK 约束 (follow_up_task→appointment, points_transaction→account/rule/order,
  points_order→product/patient, offline_event_registration→event/patient)
- critical_alert/critical_alert_response version 字段 i64→i32 统一
- vital_signs_daily_service 聚合查询添加 DeletedAt.is_null() 过滤

代码规范:
- 新增 api/upload.ts 封装文件上传,ArticleEditor 改用 service 层
- 新增 messages.updateSubscription,NotificationPreferences 改用 service 层
- 修复 erp-message SSE 测试编译错误 (移除 serde_urlencoded 依赖)
This commit is contained in:
iven
2026-05-04 11:22:54 +08:00
parent 30a578ee00
commit 444dc7dd8d
11 changed files with 245 additions and 20 deletions

View File

@@ -108,6 +108,8 @@ mod m20260504_000105_alter_patient_devices_add_status;
mod m20260504_000106_create_api_clients;
mod m20260504_000107_alter_article_article_tag_add_tenant_and_soft_delete;
mod m20260504_000108_alter_vital_signs_hourly_add_soft_delete;
mod m20260504_000109_add_missing_fk_constraints;
mod m20260504_000110_alter_critical_alerts_version_i32;
pub struct Migrator;
@@ -223,6 +225,8 @@ impl MigratorTrait for Migrator {
Box::new(m20260504_000106_create_api_clients::Migration),
Box::new(m20260504_000107_alter_article_article_tag_add_tenant_and_soft_delete::Migration),
Box::new(m20260504_000108_alter_vital_signs_hourly_add_soft_delete::Migration),
Box::new(m20260504_000109_add_missing_fk_constraints::Migration),
Box::new(m20260504_000110_alter_critical_alerts_version_i32::Migration),
]
}
}

View File

@@ -0,0 +1,129 @@
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> {
// follow_up_task.related_appointment_id → appointment.id
manager
.create_foreign_key(
ForeignKey::create()
.name("fk_follow_up_task_appointment")
.from(Alias::new("follow_up_task"), Alias::new("related_appointment_id"))
.to(Alias::new("appointment"), Alias::new("id"))
.on_delete(ForeignKeyAction::SetNull)
.to_owned(),
)
.await?;
// points_transaction.account_id → points_account.id
manager
.create_foreign_key(
ForeignKey::create()
.name("fk_points_transaction_account")
.from(Alias::new("points_transaction"), Alias::new("account_id"))
.to(Alias::new("points_account"), Alias::new("id"))
.on_delete(ForeignKeyAction::Cascade)
.to_owned(),
)
.await?;
// points_transaction.rule_id → points_rule.id
manager
.create_foreign_key(
ForeignKey::create()
.name("fk_points_transaction_rule")
.from(Alias::new("points_transaction"), Alias::new("rule_id"))
.to(Alias::new("points_rule"), Alias::new("id"))
.on_delete(ForeignKeyAction::SetNull)
.to_owned(),
)
.await?;
// points_transaction.order_id → points_order.id
manager
.create_foreign_key(
ForeignKey::create()
.name("fk_points_transaction_order")
.from(Alias::new("points_transaction"), Alias::new("order_id"))
.to(Alias::new("points_order"), Alias::new("id"))
.on_delete(ForeignKeyAction::SetNull)
.to_owned(),
)
.await?;
// points_order.product_id → points_product.id
manager
.create_foreign_key(
ForeignKey::create()
.name("fk_points_order_product")
.from(Alias::new("points_order"), Alias::new("product_id"))
.to(Alias::new("points_product"), Alias::new("id"))
.on_delete(ForeignKeyAction::Restrict)
.to_owned(),
)
.await?;
// points_order.patient_id → patient.id
manager
.create_foreign_key(
ForeignKey::create()
.name("fk_points_order_patient")
.from(Alias::new("points_order"), Alias::new("patient_id"))
.to(Alias::new("patient"), Alias::new("id"))
.on_delete(ForeignKeyAction::Cascade)
.to_owned(),
)
.await?;
// offline_event_registration.event_id → offline_event.id
manager
.create_foreign_key(
ForeignKey::create()
.name("fk_offline_event_registration_event")
.from(Alias::new("offline_event_registration"), Alias::new("event_id"))
.to(Alias::new("offline_event"), Alias::new("id"))
.on_delete(ForeignKeyAction::Cascade)
.to_owned(),
)
.await?;
// offline_event_registration.patient_id → patient.id
manager
.create_foreign_key(
ForeignKey::create()
.name("fk_offline_event_registration_patient")
.from(Alias::new("offline_event_registration"), Alias::new("patient_id"))
.to(Alias::new("patient"), Alias::new("id"))
.on_delete(ForeignKeyAction::Cascade)
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let fks = [
"fk_offline_event_registration_patient",
"fk_offline_event_registration_event",
"fk_points_order_patient",
"fk_points_order_product",
"fk_points_transaction_order",
"fk_points_transaction_rule",
"fk_points_transaction_account",
"fk_follow_up_task_appointment",
];
for fk in fks {
manager
.drop_foreign_key(
ForeignKey::drop()
.name(fk)
.table(Alias::new("dummy"))
.to_owned(),
)
.await?;
}
Ok(())
}
}

View File

@@ -0,0 +1,69 @@
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> {
// critical_alerts.version: i64 → i32
manager
.alter_table(
Table::alter()
.table(Alias::new("critical_alerts"))
.modify_column(
ColumnDef::new(Alias::new("version"))
.integer()
.not_null()
.default(1),
)
.to_owned(),
)
.await?;
// critical_alert_responses.version: i64 → i32
manager
.alter_table(
Table::alter()
.table(Alias::new("critical_alert_responses"))
.modify_column(
ColumnDef::new(Alias::new("version"))
.integer()
.not_null()
.default(1),
)
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table(Alias::new("critical_alerts"))
.modify_column(
ColumnDef::new(Alias::new("version"))
.big_integer()
.not_null()
.default(1),
)
.to_owned(),
)
.await?;
manager
.alter_table(
Table::alter()
.table(Alias::new("critical_alert_responses"))
.modify_column(
ColumnDef::new(Alias::new("version"))
.big_integer()
.not_null()
.default(1),
)
.to_owned(),
)
.await
}
}