功能修复: 1. 患者创建空名称验证:后端添加 name.trim().is_empty() 检查 2. 仪表盘统计容错:单个查询失败返回零值而非 500 3. FHIR 路由修复:从 /fhir 移到 /api/v1/fhir 保持一致 4. 冻结模块后端中间件:新增 frozen_module_middleware 拦截冻结路径 5. 积分端点权限码:health.health-data.list → health.points.list 6. 角色权限迁移:护士补充 devices.list,运营补充 points.list/manage 7. 测试结果文档:R01-R05 角色测试 + T00/T10 结果归档 Clippy 全 workspace 清零(14→0 errors): - erp-core: 修复 empty doc line、collapsible if、redundant closure 等 9 处 - erp-health: 修复 too_many_arguments、unused var、unnecessary parens 等 58 处 - erp-ai: 修复 dead_code、unused import 等 11 处 - erp-plugin: 修复 too_many_arguments、wildcard pattern 等 11 处 - erp-server-migration: 修复 enum_variant_names 5 处 - erp-auth/config/workflow/message: 各 1-3 处 工程改进: - lint-staged 配置迁移到 .lintstagedrc.js(函数式避免文件列表传给 clippy) - cargo fmt 统一格式化
1834 lines
66 KiB
Rust
1834 lines
66 KiB
Rust
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> {
|
|
// 1. patient — 患者档案
|
|
manager
|
|
.create_table(
|
|
Table::create()
|
|
.table(Patient::Table)
|
|
.if_not_exists()
|
|
.col(ColumnDef::new(Patient::Id).uuid().not_null().primary_key())
|
|
.col(ColumnDef::new(Patient::TenantId).uuid().not_null())
|
|
.col(ColumnDef::new(Patient::UserId).uuid().null())
|
|
.col(ColumnDef::new(Patient::Name).string_len(100).not_null())
|
|
.col(ColumnDef::new(Patient::Gender).string_len(10).null())
|
|
.col(ColumnDef::new(Patient::BirthDate).date().null())
|
|
.col(ColumnDef::new(Patient::BloodType).string_len(10).null())
|
|
.col(ColumnDef::new(Patient::IdNumber).string_len(20).null())
|
|
.col(ColumnDef::new(Patient::AllergyHistory).text().null())
|
|
.col(ColumnDef::new(Patient::MedicalHistorySummary).text().null())
|
|
.col(
|
|
ColumnDef::new(Patient::EmergencyContactName)
|
|
.string_len(100)
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(Patient::EmergencyContactPhone)
|
|
.string_len(20)
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(Patient::Status)
|
|
.string_len(20)
|
|
.not_null()
|
|
.default("active"),
|
|
)
|
|
.col(
|
|
ColumnDef::new(Patient::VerificationStatus)
|
|
.string_len(20)
|
|
.not_null()
|
|
.default("pending"),
|
|
)
|
|
.col(ColumnDef::new(Patient::Source).string_len(100).null())
|
|
.col(ColumnDef::new(Patient::Notes).text().null())
|
|
.col(
|
|
ColumnDef::new(Patient::CreatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(
|
|
ColumnDef::new(Patient::UpdatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(ColumnDef::new(Patient::CreatedBy).uuid().null())
|
|
.col(ColumnDef::new(Patient::UpdatedBy).uuid().null())
|
|
.col(
|
|
ColumnDef::new(Patient::DeletedAt)
|
|
.timestamp_with_time_zone()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(Patient::Version)
|
|
.integer()
|
|
.not_null()
|
|
.default(1),
|
|
)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_patient_tenant_name")
|
|
.table(Patient::Table)
|
|
.col(Patient::TenantId)
|
|
.col(Patient::Name)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_patient_tenant_status")
|
|
.table(Patient::Table)
|
|
.col(Patient::TenantId)
|
|
.col(Patient::Status)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_patient_tenant_id_number")
|
|
.table(Patient::Table)
|
|
.col(Patient::TenantId)
|
|
.col(Patient::IdNumber)
|
|
.unique()
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// 2. patient_family_member — 家庭成员
|
|
manager
|
|
.create_table(
|
|
Table::create()
|
|
.table(PatientFamilyMember::Table)
|
|
.if_not_exists()
|
|
.col(
|
|
ColumnDef::new(PatientFamilyMember::Id)
|
|
.uuid()
|
|
.not_null()
|
|
.primary_key(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientFamilyMember::TenantId)
|
|
.uuid()
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientFamilyMember::PatientId)
|
|
.uuid()
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientFamilyMember::Name)
|
|
.string_len(100)
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientFamilyMember::Relationship)
|
|
.string_len(50)
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientFamilyMember::Phone)
|
|
.string_len(20)
|
|
.null(),
|
|
)
|
|
.col(ColumnDef::new(PatientFamilyMember::BirthDate).date().null())
|
|
.col(ColumnDef::new(PatientFamilyMember::Notes).text().null())
|
|
.col(
|
|
ColumnDef::new(PatientFamilyMember::CreatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientFamilyMember::UpdatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(ColumnDef::new(PatientFamilyMember::CreatedBy).uuid().null())
|
|
.col(ColumnDef::new(PatientFamilyMember::UpdatedBy).uuid().null())
|
|
.col(
|
|
ColumnDef::new(PatientFamilyMember::DeletedAt)
|
|
.timestamp_with_time_zone()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientFamilyMember::Version)
|
|
.integer()
|
|
.not_null()
|
|
.default(1),
|
|
)
|
|
.foreign_key(
|
|
ForeignKey::create()
|
|
.from(PatientFamilyMember::Table, PatientFamilyMember::PatientId)
|
|
.to(Patient::Table, Patient::Id)
|
|
.on_delete(ForeignKeyAction::Cascade),
|
|
)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// 3. patient_tag — 患者标签
|
|
manager
|
|
.create_table(
|
|
Table::create()
|
|
.table(PatientTag::Table)
|
|
.if_not_exists()
|
|
.col(
|
|
ColumnDef::new(PatientTag::Id)
|
|
.uuid()
|
|
.not_null()
|
|
.primary_key(),
|
|
)
|
|
.col(ColumnDef::new(PatientTag::TenantId).uuid().not_null())
|
|
.col(ColumnDef::new(PatientTag::Name).string_len(50).not_null())
|
|
.col(ColumnDef::new(PatientTag::Color).string_len(20).null())
|
|
.col(ColumnDef::new(PatientTag::Description).text().null())
|
|
.col(
|
|
ColumnDef::new(PatientTag::IsSystem)
|
|
.boolean()
|
|
.not_null()
|
|
.default(false),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientTag::CreatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientTag::UpdatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(ColumnDef::new(PatientTag::CreatedBy).uuid().null())
|
|
.col(ColumnDef::new(PatientTag::UpdatedBy).uuid().null())
|
|
.col(
|
|
ColumnDef::new(PatientTag::DeletedAt)
|
|
.timestamp_with_time_zone()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientTag::Version)
|
|
.integer()
|
|
.not_null()
|
|
.default(1),
|
|
)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// 4. patient_tag_relation — 患者-标签关联
|
|
manager
|
|
.create_table(
|
|
Table::create()
|
|
.table(PatientTagRelation::Table)
|
|
.if_not_exists()
|
|
.col(
|
|
ColumnDef::new(PatientTagRelation::Id)
|
|
.uuid()
|
|
.not_null()
|
|
.primary_key(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientTagRelation::TenantId)
|
|
.uuid()
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientTagRelation::PatientId)
|
|
.uuid()
|
|
.not_null(),
|
|
)
|
|
.col(ColumnDef::new(PatientTagRelation::TagId).uuid().not_null())
|
|
.col(
|
|
ColumnDef::new(PatientTagRelation::CreatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientTagRelation::UpdatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(ColumnDef::new(PatientTagRelation::CreatedBy).uuid().null())
|
|
.col(ColumnDef::new(PatientTagRelation::UpdatedBy).uuid().null())
|
|
.col(
|
|
ColumnDef::new(PatientTagRelation::DeletedAt)
|
|
.timestamp_with_time_zone()
|
|
.null(),
|
|
)
|
|
.foreign_key(
|
|
ForeignKey::create()
|
|
.from(PatientTagRelation::Table, PatientTagRelation::PatientId)
|
|
.to(Patient::Table, Patient::Id)
|
|
.on_delete(ForeignKeyAction::Cascade),
|
|
)
|
|
.foreign_key(
|
|
ForeignKey::create()
|
|
.from(PatientTagRelation::Table, PatientTagRelation::TagId)
|
|
.to(PatientTag::Table, PatientTag::Id)
|
|
.on_delete(ForeignKeyAction::Cascade),
|
|
)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_patient_tag_rel_tenant_patient")
|
|
.table(PatientTagRelation::Table)
|
|
.col(PatientTagRelation::TenantId)
|
|
.col(PatientTagRelation::PatientId)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_patient_tag_rel_tenant_tag")
|
|
.table(PatientTagRelation::Table)
|
|
.col(PatientTagRelation::TenantId)
|
|
.col(PatientTagRelation::TagId)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// patient_tag 唯一名称索引
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_patient_tag_tenant_name_unique")
|
|
.table(PatientTag::Table)
|
|
.col(PatientTag::TenantId)
|
|
.col(PatientTag::Name)
|
|
.unique()
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// 5. doctor_profile — 医护档案
|
|
manager
|
|
.create_table(
|
|
Table::create()
|
|
.table(DoctorProfile::Table)
|
|
.if_not_exists()
|
|
.col(
|
|
ColumnDef::new(DoctorProfile::Id)
|
|
.uuid()
|
|
.not_null()
|
|
.primary_key(),
|
|
)
|
|
.col(ColumnDef::new(DoctorProfile::TenantId).uuid().not_null())
|
|
.col(ColumnDef::new(DoctorProfile::UserId).uuid().null())
|
|
.col(
|
|
ColumnDef::new(DoctorProfile::Name)
|
|
.string_len(100)
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(DoctorProfile::Department)
|
|
.string_len(100)
|
|
.null(),
|
|
)
|
|
.col(ColumnDef::new(DoctorProfile::Title).string_len(50).null())
|
|
.col(
|
|
ColumnDef::new(DoctorProfile::Specialty)
|
|
.string_len(200)
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(DoctorProfile::LicenseNumber)
|
|
.string_len(50)
|
|
.null(),
|
|
)
|
|
.col(ColumnDef::new(DoctorProfile::Bio).text().null())
|
|
.col(
|
|
ColumnDef::new(DoctorProfile::OnlineStatus)
|
|
.string_len(20)
|
|
.not_null()
|
|
.default("offline"),
|
|
)
|
|
.col(
|
|
ColumnDef::new(DoctorProfile::CreatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(
|
|
ColumnDef::new(DoctorProfile::UpdatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(ColumnDef::new(DoctorProfile::CreatedBy).uuid().null())
|
|
.col(ColumnDef::new(DoctorProfile::UpdatedBy).uuid().null())
|
|
.col(
|
|
ColumnDef::new(DoctorProfile::DeletedAt)
|
|
.timestamp_with_time_zone()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(DoctorProfile::Version)
|
|
.integer()
|
|
.not_null()
|
|
.default(1),
|
|
)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// doctor_profile 名称搜索索引
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_doctor_profile_tenant_name")
|
|
.table(DoctorProfile::Table)
|
|
.col(DoctorProfile::TenantId)
|
|
.col(DoctorProfile::Name)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// 6. patient_doctor_relation — 医患关系
|
|
manager
|
|
.create_table(
|
|
Table::create()
|
|
.table(PatientDoctorRelation::Table)
|
|
.if_not_exists()
|
|
.col(
|
|
ColumnDef::new(PatientDoctorRelation::Id)
|
|
.uuid()
|
|
.not_null()
|
|
.primary_key(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientDoctorRelation::TenantId)
|
|
.uuid()
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientDoctorRelation::PatientId)
|
|
.uuid()
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientDoctorRelation::DoctorId)
|
|
.uuid()
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientDoctorRelation::RelationshipType)
|
|
.string_len(20)
|
|
.not_null()
|
|
.default("primary"),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientDoctorRelation::CreatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientDoctorRelation::UpdatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientDoctorRelation::CreatedBy)
|
|
.uuid()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientDoctorRelation::UpdatedBy)
|
|
.uuid()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(PatientDoctorRelation::DeletedAt)
|
|
.timestamp_with_time_zone()
|
|
.null(),
|
|
)
|
|
.foreign_key(
|
|
ForeignKey::create()
|
|
.from(
|
|
PatientDoctorRelation::Table,
|
|
PatientDoctorRelation::PatientId,
|
|
)
|
|
.to(Patient::Table, Patient::Id)
|
|
.on_delete(ForeignKeyAction::Cascade),
|
|
)
|
|
.foreign_key(
|
|
ForeignKey::create()
|
|
.from(
|
|
PatientDoctorRelation::Table,
|
|
PatientDoctorRelation::DoctorId,
|
|
)
|
|
.to(DoctorProfile::Table, DoctorProfile::Id)
|
|
.on_delete(ForeignKeyAction::Cascade),
|
|
)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_patient_doctor_rel_patient")
|
|
.table(PatientDoctorRelation::Table)
|
|
.col(PatientDoctorRelation::TenantId)
|
|
.col(PatientDoctorRelation::PatientId)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_patient_doctor_rel_doctor")
|
|
.table(PatientDoctorRelation::Table)
|
|
.col(PatientDoctorRelation::TenantId)
|
|
.col(PatientDoctorRelation::DoctorId)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// 7. health_record — 体检/就诊记录
|
|
manager
|
|
.create_table(
|
|
Table::create()
|
|
.table(HealthRecord::Table)
|
|
.if_not_exists()
|
|
.col(
|
|
ColumnDef::new(HealthRecord::Id)
|
|
.uuid()
|
|
.not_null()
|
|
.primary_key(),
|
|
)
|
|
.col(ColumnDef::new(HealthRecord::TenantId).uuid().not_null())
|
|
.col(ColumnDef::new(HealthRecord::PatientId).uuid().not_null())
|
|
.col(
|
|
ColumnDef::new(HealthRecord::RecordType)
|
|
.string_len(20)
|
|
.not_null()
|
|
.default("checkup"),
|
|
)
|
|
.col(ColumnDef::new(HealthRecord::RecordDate).date().not_null())
|
|
.col(ColumnDef::new(HealthRecord::Source).string_len(200).null())
|
|
.col(
|
|
ColumnDef::new(HealthRecord::OverallAssessment)
|
|
.text()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(HealthRecord::ReportFileUrl)
|
|
.string_len(500)
|
|
.null(),
|
|
)
|
|
.col(ColumnDef::new(HealthRecord::Notes).text().null())
|
|
.col(
|
|
ColumnDef::new(HealthRecord::CreatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(
|
|
ColumnDef::new(HealthRecord::UpdatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(ColumnDef::new(HealthRecord::CreatedBy).uuid().null())
|
|
.col(ColumnDef::new(HealthRecord::UpdatedBy).uuid().null())
|
|
.col(
|
|
ColumnDef::new(HealthRecord::DeletedAt)
|
|
.timestamp_with_time_zone()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(HealthRecord::Version)
|
|
.integer()
|
|
.not_null()
|
|
.default(1),
|
|
)
|
|
.foreign_key(
|
|
ForeignKey::create()
|
|
.from(HealthRecord::Table, HealthRecord::PatientId)
|
|
.to(Patient::Table, Patient::Id)
|
|
.on_delete(ForeignKeyAction::Cascade),
|
|
)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_health_record_tenant_patient_date")
|
|
.table(HealthRecord::Table)
|
|
.col(HealthRecord::TenantId)
|
|
.col(HealthRecord::PatientId)
|
|
.col(HealthRecord::RecordDate)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// 8. vital_signs — 日常监测数据
|
|
manager
|
|
.create_table(
|
|
Table::create()
|
|
.table(VitalSigns::Table)
|
|
.if_not_exists()
|
|
.col(
|
|
ColumnDef::new(VitalSigns::Id)
|
|
.uuid()
|
|
.not_null()
|
|
.primary_key(),
|
|
)
|
|
.col(ColumnDef::new(VitalSigns::TenantId).uuid().not_null())
|
|
.col(ColumnDef::new(VitalSigns::PatientId).uuid().not_null())
|
|
.col(ColumnDef::new(VitalSigns::RecordDate).date().not_null())
|
|
.col(
|
|
ColumnDef::new(VitalSigns::SystolicBpMorning)
|
|
.integer()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(VitalSigns::DiastolicBpMorning)
|
|
.integer()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(VitalSigns::SystolicBpEvening)
|
|
.integer()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(VitalSigns::DiastolicBpEvening)
|
|
.integer()
|
|
.null(),
|
|
)
|
|
.col(ColumnDef::new(VitalSigns::HeartRate).integer().null())
|
|
.col(ColumnDef::new(VitalSigns::Weight).decimal_len(5, 1).null())
|
|
.col(
|
|
ColumnDef::new(VitalSigns::BloodSugar)
|
|
.decimal_len(5, 1)
|
|
.null(),
|
|
)
|
|
.col(ColumnDef::new(VitalSigns::WaterIntakeMl).integer().null())
|
|
.col(ColumnDef::new(VitalSigns::UrineOutputMl).integer().null())
|
|
.col(ColumnDef::new(VitalSigns::Notes).text().null())
|
|
.col(
|
|
ColumnDef::new(VitalSigns::CreatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(
|
|
ColumnDef::new(VitalSigns::UpdatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(ColumnDef::new(VitalSigns::CreatedBy).uuid().null())
|
|
.col(ColumnDef::new(VitalSigns::UpdatedBy).uuid().null())
|
|
.col(
|
|
ColumnDef::new(VitalSigns::DeletedAt)
|
|
.timestamp_with_time_zone()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(VitalSigns::Version)
|
|
.integer()
|
|
.not_null()
|
|
.default(1),
|
|
)
|
|
.foreign_key(
|
|
ForeignKey::create()
|
|
.from(VitalSigns::Table, VitalSigns::PatientId)
|
|
.to(Patient::Table, Patient::Id)
|
|
.on_delete(ForeignKeyAction::Cascade),
|
|
)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_vital_signs_tenant_patient_date")
|
|
.table(VitalSigns::Table)
|
|
.col(VitalSigns::TenantId)
|
|
.col(VitalSigns::PatientId)
|
|
.col(VitalSigns::RecordDate)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// 9. lab_report — 化验报告
|
|
manager
|
|
.create_table(
|
|
Table::create()
|
|
.table(LabReport::Table)
|
|
.if_not_exists()
|
|
.col(
|
|
ColumnDef::new(LabReport::Id)
|
|
.uuid()
|
|
.not_null()
|
|
.primary_key(),
|
|
)
|
|
.col(ColumnDef::new(LabReport::TenantId).uuid().not_null())
|
|
.col(ColumnDef::new(LabReport::PatientId).uuid().not_null())
|
|
.col(ColumnDef::new(LabReport::ReportDate).date().not_null())
|
|
.col(
|
|
ColumnDef::new(LabReport::ReportType)
|
|
.string_len(50)
|
|
.not_null(),
|
|
)
|
|
.col(ColumnDef::new(LabReport::Indicators).json_binary().null())
|
|
.col(ColumnDef::new(LabReport::ImageUrls).json_binary().null())
|
|
.col(
|
|
ColumnDef::new(LabReport::DoctorInterpretation)
|
|
.text()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(LabReport::CreatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(
|
|
ColumnDef::new(LabReport::UpdatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(ColumnDef::new(LabReport::CreatedBy).uuid().null())
|
|
.col(ColumnDef::new(LabReport::UpdatedBy).uuid().null())
|
|
.col(
|
|
ColumnDef::new(LabReport::DeletedAt)
|
|
.timestamp_with_time_zone()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(LabReport::Version)
|
|
.integer()
|
|
.not_null()
|
|
.default(1),
|
|
)
|
|
.foreign_key(
|
|
ForeignKey::create()
|
|
.from(LabReport::Table, LabReport::PatientId)
|
|
.to(Patient::Table, Patient::Id)
|
|
.on_delete(ForeignKeyAction::Cascade),
|
|
)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_lab_report_tenant_patient_date")
|
|
.table(LabReport::Table)
|
|
.col(LabReport::TenantId)
|
|
.col(LabReport::PatientId)
|
|
.col(LabReport::ReportDate)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// 10. health_trend — 健康趋势报告
|
|
manager
|
|
.create_table(
|
|
Table::create()
|
|
.table(HealthTrend::Table)
|
|
.if_not_exists()
|
|
.col(
|
|
ColumnDef::new(HealthTrend::Id)
|
|
.uuid()
|
|
.not_null()
|
|
.primary_key(),
|
|
)
|
|
.col(ColumnDef::new(HealthTrend::TenantId).uuid().not_null())
|
|
.col(ColumnDef::new(HealthTrend::PatientId).uuid().not_null())
|
|
.col(ColumnDef::new(HealthTrend::PeriodStart).date().not_null())
|
|
.col(ColumnDef::new(HealthTrend::PeriodEnd).date().not_null())
|
|
.col(
|
|
ColumnDef::new(HealthTrend::IndicatorSummary)
|
|
.json_binary()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(HealthTrend::AbnormalItems)
|
|
.json_binary()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(HealthTrend::GenerationType)
|
|
.string_len(20)
|
|
.not_null()
|
|
.default("auto"),
|
|
)
|
|
.col(
|
|
ColumnDef::new(HealthTrend::ReportFileUrl)
|
|
.string_len(500)
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(HealthTrend::CreatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(
|
|
ColumnDef::new(HealthTrend::UpdatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(ColumnDef::new(HealthTrend::CreatedBy).uuid().null())
|
|
.col(ColumnDef::new(HealthTrend::UpdatedBy).uuid().null())
|
|
.col(
|
|
ColumnDef::new(HealthTrend::DeletedAt)
|
|
.timestamp_with_time_zone()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(HealthTrend::Version)
|
|
.integer()
|
|
.not_null()
|
|
.default(1),
|
|
)
|
|
.foreign_key(
|
|
ForeignKey::create()
|
|
.from(HealthTrend::Table, HealthTrend::PatientId)
|
|
.to(Patient::Table, Patient::Id)
|
|
.on_delete(ForeignKeyAction::Cascade),
|
|
)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// 11. appointment — 预约记录
|
|
manager
|
|
.create_table(
|
|
Table::create()
|
|
.table(Appointment::Table)
|
|
.if_not_exists()
|
|
.col(
|
|
ColumnDef::new(Appointment::Id)
|
|
.uuid()
|
|
.not_null()
|
|
.primary_key(),
|
|
)
|
|
.col(ColumnDef::new(Appointment::TenantId).uuid().not_null())
|
|
.col(ColumnDef::new(Appointment::PatientId).uuid().not_null())
|
|
.col(ColumnDef::new(Appointment::DoctorId).uuid().null())
|
|
.col(
|
|
ColumnDef::new(Appointment::AppointmentType)
|
|
.string_len(20)
|
|
.not_null()
|
|
.default("outpatient"),
|
|
)
|
|
.col(
|
|
ColumnDef::new(Appointment::AppointmentDate)
|
|
.date()
|
|
.not_null(),
|
|
)
|
|
.col(ColumnDef::new(Appointment::StartTime).time().not_null())
|
|
.col(ColumnDef::new(Appointment::EndTime).time().not_null())
|
|
.col(
|
|
ColumnDef::new(Appointment::Status)
|
|
.string_len(20)
|
|
.not_null()
|
|
.default("pending"),
|
|
)
|
|
.col(ColumnDef::new(Appointment::CancelReason).text().null())
|
|
.col(ColumnDef::new(Appointment::Notes).text().null())
|
|
.col(
|
|
ColumnDef::new(Appointment::CreatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(
|
|
ColumnDef::new(Appointment::UpdatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(ColumnDef::new(Appointment::CreatedBy).uuid().null())
|
|
.col(ColumnDef::new(Appointment::UpdatedBy).uuid().null())
|
|
.col(
|
|
ColumnDef::new(Appointment::DeletedAt)
|
|
.timestamp_with_time_zone()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(Appointment::Version)
|
|
.integer()
|
|
.not_null()
|
|
.default(1),
|
|
)
|
|
.foreign_key(
|
|
ForeignKey::create()
|
|
.from(Appointment::Table, Appointment::PatientId)
|
|
.to(Patient::Table, Patient::Id)
|
|
.on_delete(ForeignKeyAction::Cascade),
|
|
)
|
|
.foreign_key(
|
|
ForeignKey::create()
|
|
.from(Appointment::Table, Appointment::DoctorId)
|
|
.to(DoctorProfile::Table, DoctorProfile::Id)
|
|
.on_delete(ForeignKeyAction::SetNull),
|
|
)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_appointment_tenant_date_status")
|
|
.table(Appointment::Table)
|
|
.col(Appointment::TenantId)
|
|
.col(Appointment::AppointmentDate)
|
|
.col(Appointment::Status)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_appointment_tenant_doctor_date")
|
|
.table(Appointment::Table)
|
|
.col(Appointment::TenantId)
|
|
.col(Appointment::DoctorId)
|
|
.col(Appointment::AppointmentDate)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// 12. doctor_schedule — 医生排班
|
|
manager
|
|
.create_table(
|
|
Table::create()
|
|
.table(DoctorSchedule::Table)
|
|
.if_not_exists()
|
|
.col(
|
|
ColumnDef::new(DoctorSchedule::Id)
|
|
.uuid()
|
|
.not_null()
|
|
.primary_key(),
|
|
)
|
|
.col(ColumnDef::new(DoctorSchedule::TenantId).uuid().not_null())
|
|
.col(ColumnDef::new(DoctorSchedule::DoctorId).uuid().not_null())
|
|
.col(
|
|
ColumnDef::new(DoctorSchedule::ScheduleDate)
|
|
.date()
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(DoctorSchedule::PeriodType)
|
|
.string_len(20)
|
|
.not_null()
|
|
.default("am"),
|
|
)
|
|
.col(ColumnDef::new(DoctorSchedule::StartTime).time().not_null())
|
|
.col(ColumnDef::new(DoctorSchedule::EndTime).time().not_null())
|
|
.col(
|
|
ColumnDef::new(DoctorSchedule::MaxAppointments)
|
|
.integer()
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(DoctorSchedule::CurrentAppointments)
|
|
.integer()
|
|
.not_null()
|
|
.default(0),
|
|
)
|
|
.col(
|
|
ColumnDef::new(DoctorSchedule::Status)
|
|
.string_len(20)
|
|
.not_null()
|
|
.default("enabled"),
|
|
)
|
|
.col(
|
|
ColumnDef::new(DoctorSchedule::CreatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(
|
|
ColumnDef::new(DoctorSchedule::UpdatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(ColumnDef::new(DoctorSchedule::CreatedBy).uuid().null())
|
|
.col(ColumnDef::new(DoctorSchedule::UpdatedBy).uuid().null())
|
|
.col(
|
|
ColumnDef::new(DoctorSchedule::DeletedAt)
|
|
.timestamp_with_time_zone()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(DoctorSchedule::Version)
|
|
.integer()
|
|
.not_null()
|
|
.default(1),
|
|
)
|
|
.foreign_key(
|
|
ForeignKey::create()
|
|
.from(DoctorSchedule::Table, DoctorSchedule::DoctorId)
|
|
.to(DoctorProfile::Table, DoctorProfile::Id)
|
|
.on_delete(ForeignKeyAction::Cascade),
|
|
)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_doctor_schedule_tenant_doctor_date")
|
|
.table(DoctorSchedule::Table)
|
|
.col(DoctorSchedule::TenantId)
|
|
.col(DoctorSchedule::DoctorId)
|
|
.col(DoctorSchedule::ScheduleDate)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// doctor_schedule 唯一约束:同一医生同一天同一时段不能重复排班
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_doctor_schedule_unique_slot")
|
|
.table(DoctorSchedule::Table)
|
|
.col(DoctorSchedule::TenantId)
|
|
.col(DoctorSchedule::DoctorId)
|
|
.col(DoctorSchedule::ScheduleDate)
|
|
.col(DoctorSchedule::StartTime)
|
|
.unique()
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// 13. follow_up_task — 随访任务
|
|
manager
|
|
.create_table(
|
|
Table::create()
|
|
.table(FollowUpTask::Table)
|
|
.if_not_exists()
|
|
.col(
|
|
ColumnDef::new(FollowUpTask::Id)
|
|
.uuid()
|
|
.not_null()
|
|
.primary_key(),
|
|
)
|
|
.col(ColumnDef::new(FollowUpTask::TenantId).uuid().not_null())
|
|
.col(ColumnDef::new(FollowUpTask::PatientId).uuid().not_null())
|
|
.col(ColumnDef::new(FollowUpTask::AssignedTo).uuid().null())
|
|
.col(
|
|
ColumnDef::new(FollowUpTask::FollowUpType)
|
|
.string_len(20)
|
|
.not_null()
|
|
.default("phone"),
|
|
)
|
|
.col(ColumnDef::new(FollowUpTask::PlannedDate).date().not_null())
|
|
.col(
|
|
ColumnDef::new(FollowUpTask::Status)
|
|
.string_len(20)
|
|
.not_null()
|
|
.default("pending"),
|
|
)
|
|
.col(ColumnDef::new(FollowUpTask::ContentTemplate).text().null())
|
|
.col(
|
|
ColumnDef::new(FollowUpTask::RelatedAppointmentId)
|
|
.uuid()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(FollowUpTask::CreatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(
|
|
ColumnDef::new(FollowUpTask::UpdatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(ColumnDef::new(FollowUpTask::CreatedBy).uuid().null())
|
|
.col(ColumnDef::new(FollowUpTask::UpdatedBy).uuid().null())
|
|
.col(
|
|
ColumnDef::new(FollowUpTask::DeletedAt)
|
|
.timestamp_with_time_zone()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(FollowUpTask::Version)
|
|
.integer()
|
|
.not_null()
|
|
.default(1),
|
|
)
|
|
.foreign_key(
|
|
ForeignKey::create()
|
|
.from(FollowUpTask::Table, FollowUpTask::PatientId)
|
|
.to(Patient::Table, Patient::Id)
|
|
.on_delete(ForeignKeyAction::Cascade),
|
|
)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_follow_up_task_tenant_assigned_status")
|
|
.table(FollowUpTask::Table)
|
|
.col(FollowUpTask::TenantId)
|
|
.col(FollowUpTask::AssignedTo)
|
|
.col(FollowUpTask::Status)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_follow_up_task_tenant_date_status")
|
|
.table(FollowUpTask::Table)
|
|
.col(FollowUpTask::TenantId)
|
|
.col(FollowUpTask::PlannedDate)
|
|
.col(FollowUpTask::Status)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// 14. follow_up_record — 随访记录
|
|
manager
|
|
.create_table(
|
|
Table::create()
|
|
.table(FollowUpRecord::Table)
|
|
.if_not_exists()
|
|
.col(
|
|
ColumnDef::new(FollowUpRecord::Id)
|
|
.uuid()
|
|
.not_null()
|
|
.primary_key(),
|
|
)
|
|
.col(ColumnDef::new(FollowUpRecord::TenantId).uuid().not_null())
|
|
.col(ColumnDef::new(FollowUpRecord::TaskId).uuid().not_null())
|
|
.col(ColumnDef::new(FollowUpRecord::ExecutedBy).uuid().null())
|
|
.col(
|
|
ColumnDef::new(FollowUpRecord::ExecutedDate)
|
|
.date()
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(FollowUpRecord::Result)
|
|
.string_len(20)
|
|
.not_null()
|
|
.default("followed_up"),
|
|
)
|
|
.col(
|
|
ColumnDef::new(FollowUpRecord::PatientCondition)
|
|
.text()
|
|
.null(),
|
|
)
|
|
.col(ColumnDef::new(FollowUpRecord::MedicalAdvice).text().null())
|
|
.col(
|
|
ColumnDef::new(FollowUpRecord::NextFollowUpDate)
|
|
.date()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(FollowUpRecord::CreatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(
|
|
ColumnDef::new(FollowUpRecord::UpdatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(ColumnDef::new(FollowUpRecord::CreatedBy).uuid().null())
|
|
.col(ColumnDef::new(FollowUpRecord::UpdatedBy).uuid().null())
|
|
.col(
|
|
ColumnDef::new(FollowUpRecord::DeletedAt)
|
|
.timestamp_with_time_zone()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(FollowUpRecord::Version)
|
|
.integer()
|
|
.not_null()
|
|
.default(1),
|
|
)
|
|
.foreign_key(
|
|
ForeignKey::create()
|
|
.from(FollowUpRecord::Table, FollowUpRecord::TaskId)
|
|
.to(FollowUpTask::Table, FollowUpTask::Id)
|
|
.on_delete(ForeignKeyAction::Cascade),
|
|
)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_follow_up_record_tenant_task")
|
|
.table(FollowUpRecord::Table)
|
|
.col(FollowUpRecord::TenantId)
|
|
.col(FollowUpRecord::TaskId)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// 15. consultation_session — 咨询会话
|
|
manager
|
|
.create_table(
|
|
Table::create()
|
|
.table(ConsultationSession::Table)
|
|
.if_not_exists()
|
|
.col(
|
|
ColumnDef::new(ConsultationSession::Id)
|
|
.uuid()
|
|
.not_null()
|
|
.primary_key(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationSession::TenantId)
|
|
.uuid()
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationSession::PatientId)
|
|
.uuid()
|
|
.not_null(),
|
|
)
|
|
.col(ColumnDef::new(ConsultationSession::DoctorId).uuid().null())
|
|
.col(
|
|
ColumnDef::new(ConsultationSession::ConsultationType)
|
|
.string_len(20)
|
|
.not_null()
|
|
.default("customer_service"),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationSession::Status)
|
|
.string_len(20)
|
|
.not_null()
|
|
.default("waiting"),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationSession::LastMessageAt)
|
|
.timestamp_with_time_zone()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationSession::UnreadCountPatient)
|
|
.integer()
|
|
.not_null()
|
|
.default(0),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationSession::UnreadCountDoctor)
|
|
.integer()
|
|
.not_null()
|
|
.default(0),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationSession::CreatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationSession::UpdatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(ColumnDef::new(ConsultationSession::CreatedBy).uuid().null())
|
|
.col(ColumnDef::new(ConsultationSession::UpdatedBy).uuid().null())
|
|
.col(
|
|
ColumnDef::new(ConsultationSession::DeletedAt)
|
|
.timestamp_with_time_zone()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationSession::Version)
|
|
.integer()
|
|
.not_null()
|
|
.default(1),
|
|
)
|
|
.foreign_key(
|
|
ForeignKey::create()
|
|
.from(ConsultationSession::Table, ConsultationSession::PatientId)
|
|
.to(Patient::Table, Patient::Id)
|
|
.on_delete(ForeignKeyAction::Cascade),
|
|
)
|
|
.foreign_key(
|
|
ForeignKey::create()
|
|
.from(ConsultationSession::Table, ConsultationSession::DoctorId)
|
|
.to(DoctorProfile::Table, DoctorProfile::Id)
|
|
.on_delete(ForeignKeyAction::SetNull),
|
|
)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_consultation_session_tenant_doctor_status")
|
|
.table(ConsultationSession::Table)
|
|
.col(ConsultationSession::TenantId)
|
|
.col(ConsultationSession::DoctorId)
|
|
.col(ConsultationSession::Status)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_consultation_session_tenant_patient_status")
|
|
.table(ConsultationSession::Table)
|
|
.col(ConsultationSession::TenantId)
|
|
.col(ConsultationSession::PatientId)
|
|
.col(ConsultationSession::Status)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// 16. consultation_message — 咨询消息
|
|
manager
|
|
.create_table(
|
|
Table::create()
|
|
.table(ConsultationMessage::Table)
|
|
.if_not_exists()
|
|
.col(
|
|
ColumnDef::new(ConsultationMessage::Id)
|
|
.uuid()
|
|
.not_null()
|
|
.primary_key(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationMessage::TenantId)
|
|
.uuid()
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationMessage::SessionId)
|
|
.uuid()
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationMessage::SenderId)
|
|
.uuid()
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationMessage::SenderRole)
|
|
.string_len(20)
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationMessage::ContentType)
|
|
.string_len(20)
|
|
.not_null()
|
|
.default("text"),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationMessage::Content)
|
|
.text()
|
|
.not_null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationMessage::IsRead)
|
|
.boolean()
|
|
.not_null()
|
|
.default(false),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationMessage::CreatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationMessage::UpdatedAt)
|
|
.timestamp_with_time_zone()
|
|
.not_null()
|
|
.default(Expr::current_timestamp()),
|
|
)
|
|
.col(ColumnDef::new(ConsultationMessage::CreatedBy).uuid().null())
|
|
.col(ColumnDef::new(ConsultationMessage::UpdatedBy).uuid().null())
|
|
.col(
|
|
ColumnDef::new(ConsultationMessage::DeletedAt)
|
|
.timestamp_with_time_zone()
|
|
.null(),
|
|
)
|
|
.col(
|
|
ColumnDef::new(ConsultationMessage::Version)
|
|
.integer()
|
|
.not_null()
|
|
.default(1),
|
|
)
|
|
.foreign_key(
|
|
ForeignKey::create()
|
|
.from(ConsultationMessage::Table, ConsultationMessage::SessionId)
|
|
.to(ConsultationSession::Table, ConsultationSession::Id)
|
|
.on_delete(ForeignKeyAction::Cascade),
|
|
)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_consultation_message_tenant_session_created")
|
|
.table(ConsultationMessage::Table)
|
|
.col(ConsultationMessage::TenantId)
|
|
.col(ConsultationMessage::SessionId)
|
|
.col(ConsultationMessage::CreatedAt)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
// health_trend 索引
|
|
manager
|
|
.create_index(
|
|
Index::create()
|
|
.if_not_exists()
|
|
.name("idx_health_trend_tenant_patient")
|
|
.table(HealthTrend::Table)
|
|
.col(HealthTrend::TenantId)
|
|
.col(HealthTrend::PatientId)
|
|
.to_owned(),
|
|
)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
|
manager
|
|
.drop_table(Table::drop().table(ConsultationMessage::Table).to_owned())
|
|
.await?;
|
|
manager
|
|
.drop_table(Table::drop().table(ConsultationSession::Table).to_owned())
|
|
.await?;
|
|
manager
|
|
.drop_table(Table::drop().table(FollowUpRecord::Table).to_owned())
|
|
.await?;
|
|
manager
|
|
.drop_table(Table::drop().table(FollowUpTask::Table).to_owned())
|
|
.await?;
|
|
manager
|
|
.drop_table(Table::drop().table(Appointment::Table).to_owned())
|
|
.await?;
|
|
manager
|
|
.drop_table(Table::drop().table(DoctorSchedule::Table).to_owned())
|
|
.await?;
|
|
manager
|
|
.drop_table(Table::drop().table(HealthTrend::Table).to_owned())
|
|
.await?;
|
|
manager
|
|
.drop_table(Table::drop().table(LabReport::Table).to_owned())
|
|
.await?;
|
|
manager
|
|
.drop_table(Table::drop().table(VitalSigns::Table).to_owned())
|
|
.await?;
|
|
manager
|
|
.drop_table(Table::drop().table(HealthRecord::Table).to_owned())
|
|
.await?;
|
|
manager
|
|
.drop_table(Table::drop().table(PatientDoctorRelation::Table).to_owned())
|
|
.await?;
|
|
manager
|
|
.drop_table(Table::drop().table(PatientTagRelation::Table).to_owned())
|
|
.await?;
|
|
manager
|
|
.drop_table(Table::drop().table(PatientTag::Table).to_owned())
|
|
.await?;
|
|
manager
|
|
.drop_table(Table::drop().table(PatientFamilyMember::Table).to_owned())
|
|
.await?;
|
|
manager
|
|
.drop_table(Table::drop().table(DoctorProfile::Table).to_owned())
|
|
.await?;
|
|
manager
|
|
.drop_table(Table::drop().table(Patient::Table).to_owned())
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(DeriveIden)]
|
|
enum Patient {
|
|
Table,
|
|
Id,
|
|
TenantId,
|
|
UserId,
|
|
Name,
|
|
Gender,
|
|
BirthDate,
|
|
BloodType,
|
|
IdNumber,
|
|
AllergyHistory,
|
|
MedicalHistorySummary,
|
|
EmergencyContactName,
|
|
EmergencyContactPhone,
|
|
Status,
|
|
VerificationStatus,
|
|
Source,
|
|
Notes,
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
CreatedBy,
|
|
UpdatedBy,
|
|
DeletedAt,
|
|
Version,
|
|
}
|
|
|
|
#[derive(DeriveIden)]
|
|
enum PatientFamilyMember {
|
|
Table,
|
|
Id,
|
|
TenantId,
|
|
PatientId,
|
|
Name,
|
|
Relationship,
|
|
Phone,
|
|
BirthDate,
|
|
Notes,
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
CreatedBy,
|
|
UpdatedBy,
|
|
DeletedAt,
|
|
Version,
|
|
}
|
|
|
|
#[derive(DeriveIden)]
|
|
enum PatientTag {
|
|
Table,
|
|
Id,
|
|
TenantId,
|
|
Name,
|
|
Color,
|
|
Description,
|
|
IsSystem,
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
CreatedBy,
|
|
UpdatedBy,
|
|
DeletedAt,
|
|
Version,
|
|
}
|
|
|
|
#[derive(DeriveIden)]
|
|
enum PatientTagRelation {
|
|
Table,
|
|
Id,
|
|
TenantId,
|
|
PatientId,
|
|
TagId,
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
CreatedBy,
|
|
UpdatedBy,
|
|
DeletedAt,
|
|
}
|
|
|
|
#[derive(DeriveIden)]
|
|
enum DoctorProfile {
|
|
Table,
|
|
Id,
|
|
TenantId,
|
|
UserId,
|
|
Name,
|
|
Department,
|
|
Title,
|
|
Specialty,
|
|
LicenseNumber,
|
|
Bio,
|
|
OnlineStatus,
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
CreatedBy,
|
|
UpdatedBy,
|
|
DeletedAt,
|
|
Version,
|
|
}
|
|
|
|
#[derive(DeriveIden)]
|
|
enum PatientDoctorRelation {
|
|
Table,
|
|
Id,
|
|
TenantId,
|
|
PatientId,
|
|
DoctorId,
|
|
RelationshipType,
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
CreatedBy,
|
|
UpdatedBy,
|
|
DeletedAt,
|
|
}
|
|
|
|
#[derive(DeriveIden)]
|
|
enum HealthRecord {
|
|
Table,
|
|
Id,
|
|
TenantId,
|
|
PatientId,
|
|
RecordType,
|
|
RecordDate,
|
|
Source,
|
|
OverallAssessment,
|
|
ReportFileUrl,
|
|
Notes,
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
CreatedBy,
|
|
UpdatedBy,
|
|
DeletedAt,
|
|
Version,
|
|
}
|
|
|
|
#[derive(DeriveIden)]
|
|
enum VitalSigns {
|
|
Table,
|
|
Id,
|
|
TenantId,
|
|
PatientId,
|
|
RecordDate,
|
|
SystolicBpMorning,
|
|
DiastolicBpMorning,
|
|
SystolicBpEvening,
|
|
DiastolicBpEvening,
|
|
HeartRate,
|
|
Weight,
|
|
BloodSugar,
|
|
WaterIntakeMl,
|
|
UrineOutputMl,
|
|
Notes,
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
CreatedBy,
|
|
UpdatedBy,
|
|
DeletedAt,
|
|
Version,
|
|
}
|
|
|
|
#[derive(DeriveIden)]
|
|
enum LabReport {
|
|
Table,
|
|
Id,
|
|
TenantId,
|
|
PatientId,
|
|
ReportDate,
|
|
ReportType,
|
|
Indicators,
|
|
ImageUrls,
|
|
DoctorInterpretation,
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
CreatedBy,
|
|
UpdatedBy,
|
|
DeletedAt,
|
|
Version,
|
|
}
|
|
|
|
#[derive(DeriveIden)]
|
|
enum HealthTrend {
|
|
Table,
|
|
Id,
|
|
TenantId,
|
|
PatientId,
|
|
PeriodStart,
|
|
PeriodEnd,
|
|
IndicatorSummary,
|
|
AbnormalItems,
|
|
GenerationType,
|
|
ReportFileUrl,
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
CreatedBy,
|
|
UpdatedBy,
|
|
DeletedAt,
|
|
Version,
|
|
}
|
|
|
|
#[derive(DeriveIden)]
|
|
#[allow(clippy::enum_variant_names)]
|
|
enum Appointment {
|
|
Table,
|
|
Id,
|
|
TenantId,
|
|
PatientId,
|
|
DoctorId,
|
|
AppointmentType,
|
|
AppointmentDate,
|
|
StartTime,
|
|
EndTime,
|
|
Status,
|
|
CancelReason,
|
|
Notes,
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
CreatedBy,
|
|
UpdatedBy,
|
|
DeletedAt,
|
|
Version,
|
|
}
|
|
|
|
#[derive(DeriveIden)]
|
|
enum DoctorSchedule {
|
|
Table,
|
|
Id,
|
|
TenantId,
|
|
DoctorId,
|
|
ScheduleDate,
|
|
PeriodType,
|
|
StartTime,
|
|
EndTime,
|
|
MaxAppointments,
|
|
CurrentAppointments,
|
|
Status,
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
CreatedBy,
|
|
UpdatedBy,
|
|
DeletedAt,
|
|
Version,
|
|
}
|
|
|
|
#[derive(DeriveIden)]
|
|
enum FollowUpTask {
|
|
Table,
|
|
Id,
|
|
TenantId,
|
|
PatientId,
|
|
AssignedTo,
|
|
FollowUpType,
|
|
PlannedDate,
|
|
Status,
|
|
ContentTemplate,
|
|
RelatedAppointmentId,
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
CreatedBy,
|
|
UpdatedBy,
|
|
DeletedAt,
|
|
Version,
|
|
}
|
|
|
|
#[derive(DeriveIden)]
|
|
enum FollowUpRecord {
|
|
Table,
|
|
Id,
|
|
TenantId,
|
|
TaskId,
|
|
ExecutedBy,
|
|
ExecutedDate,
|
|
Result,
|
|
PatientCondition,
|
|
MedicalAdvice,
|
|
NextFollowUpDate,
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
CreatedBy,
|
|
UpdatedBy,
|
|
DeletedAt,
|
|
Version,
|
|
}
|
|
|
|
#[derive(DeriveIden)]
|
|
enum ConsultationSession {
|
|
Table,
|
|
Id,
|
|
TenantId,
|
|
PatientId,
|
|
DoctorId,
|
|
ConsultationType,
|
|
Status,
|
|
LastMessageAt,
|
|
UnreadCountPatient,
|
|
UnreadCountDoctor,
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
CreatedBy,
|
|
UpdatedBy,
|
|
DeletedAt,
|
|
Version,
|
|
}
|
|
|
|
#[derive(DeriveIden)]
|
|
enum ConsultationMessage {
|
|
Table,
|
|
Id,
|
|
TenantId,
|
|
SessionId,
|
|
SenderId,
|
|
SenderRole,
|
|
ContentType,
|
|
Content,
|
|
IsRead,
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
CreatedBy,
|
|
UpdatedBy,
|
|
DeletedAt,
|
|
Version,
|
|
}
|