fix: 多角色业务链路测试发现并修复 3 类问题
1. 角色权限修复(CRITICAL): - operator 角色权限为空(迁移 name/code 不匹配 + 软删除冲突) - doctor 角色权限被误清(API assign_permissions 失败导致全部软删除) - nurse 缺 devices 权限 + doctor/nurse 缺 appointment 权限 - 新增 3 个迁移 000130-000132 修复所有角色权限 2. 趋势指标映射修复(HIGH): - 前端 blood_pressure_systolic → systolic_bp_morning - 前端 blood_sugar_fasting → blood_sugar - 同步修复首页、健康页、趋势页的 indicator 参数 3. 咨询页错误处理优化(MEDIUM): - 403/401 时显示空列表而非"加载失败"错误提示
This commit is contained in:
@@ -131,6 +131,9 @@ mod m20260506_000126_fix_role_permissions_cleanup;
|
||||
mod m20260507_000127_fix_doctor_extra_permissions;
|
||||
mod m20260507_000128_fix_alert_status_and_menu_perms;
|
||||
mod m20260507_000129_fix_nurse_operator_points_permissions;
|
||||
mod m20260508_000130_fix_operator_permissions_and_nurse_devices;
|
||||
mod m20260508_000131_fix_all_role_permissions;
|
||||
mod m20260508_000132_fix_doctor_permissions_restore;
|
||||
|
||||
pub struct Migrator;
|
||||
|
||||
@@ -269,6 +272,9 @@ impl MigratorTrait for Migrator {
|
||||
Box::new(m20260507_000127_fix_doctor_extra_permissions::Migration),
|
||||
Box::new(m20260507_000128_fix_alert_status_and_menu_perms::Migration),
|
||||
Box::new(m20260507_000129_fix_nurse_operator_points_permissions::Migration),
|
||||
Box::new(m20260508_000130_fix_operator_permissions_and_nurse_devices::Migration),
|
||||
Box::new(m20260508_000131_fix_all_role_permissions::Migration),
|
||||
Box::new(m20260508_000132_fix_doctor_permissions_restore::Migration),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
//! 迁移桩文件 — 000130 的实际逻辑已合并到 000131
|
||||
//! 保留此文件以避免 "missing migration file" 错误
|
||||
|
||||
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> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
//! 修复所有角色权限:operator 为空 + doctor 被误清 + nurse 缺 devices + 补 appointment
|
||||
//!
|
||||
//! 根因链:
|
||||
//! 1. m20260507_000129 SQL 使用 `r.name = 'nurse'`(中文),导致 WHERE 不匹配
|
||||
//! 2. assign_permissions API 先软删除再 INSERT,INSERT 失败(唯一约束)导致权限全部丢失
|
||||
//! 3. 000130 迁移的 `DELETE WHERE deleted_at IS NOT NULL` 物理删除了被软删除的记录
|
||||
//!
|
||||
//! 本迁移:物理清理后,对所有受影响角色重新分配完整权限。
|
||||
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
async fn assign_perms(
|
||||
db: &sea_orm_migration::SchemaManagerConnection<'_>,
|
||||
role_code: &str,
|
||||
perm_codes: &[&str],
|
||||
) -> Result<(), DbErr> {
|
||||
for code in perm_codes {
|
||||
db.execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
format!(
|
||||
"INSERT INTO role_permissions (role_id, permission_id, tenant_id, data_scope, created_at, updated_at, created_by, updated_by, deleted_at, version) \
|
||||
SELECT r.id, p.id, r.tenant_id, 'all', NOW(), NOW(), r.id, r.id, NULL, 1 \
|
||||
FROM roles r \
|
||||
JOIN permissions p ON p.tenant_id = r.tenant_id AND p.code = '{code}' AND p.deleted_at IS NULL \
|
||||
WHERE r.code = '{role_code}' AND r.deleted_at IS NULL \
|
||||
ON CONFLICT (role_id, permission_id) DO UPDATE SET \
|
||||
data_scope = 'all', deleted_at = NULL, updated_at = NOW(), version = role_permissions.version + 1"
|
||||
),
|
||||
)).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
let db = manager.get_connection();
|
||||
|
||||
// === 1. 物理删除所有被软删除的 role_permissions 记录 ===
|
||||
db.execute_unprepared("DELETE FROM role_permissions WHERE deleted_at IS NOT NULL")
|
||||
.await?;
|
||||
|
||||
// === 2. Doctor 角色:完整权限重新分配 ===
|
||||
let doctor_perms = vec![
|
||||
"health.patient.list",
|
||||
"health.patient.manage",
|
||||
"health.health-data.list",
|
||||
"health.follow-up.list",
|
||||
"health.follow-up.manage",
|
||||
"health.consultation.list",
|
||||
"health.consultation.manage",
|
||||
"health.doctor.list",
|
||||
"health.doctor.manage",
|
||||
"health.action-inbox.list",
|
||||
"health.action-inbox.manage",
|
||||
"health.daily-monitoring.list",
|
||||
"health.daily-monitoring.manage",
|
||||
"health.alerts.list",
|
||||
"health.alerts.manage",
|
||||
"health.alert-rules.list",
|
||||
"health.critical-alerts.list",
|
||||
"health.diagnosis.list",
|
||||
"health.diagnosis.manage",
|
||||
"health.consent.list",
|
||||
"health.consent.manage",
|
||||
"health.follow-up-templates.list",
|
||||
"health.follow-up-templates.manage",
|
||||
"health.appointment.list",
|
||||
"health.appointment.manage",
|
||||
"health.care-plan.list",
|
||||
"health.care-plan.manage",
|
||||
"health.dialysis.list",
|
||||
"health.dialysis.manage",
|
||||
"health.dialysis-prescription.list",
|
||||
"health.dialysis-prescription.manage",
|
||||
"health.dialysis.stats",
|
||||
"ai.analysis.list",
|
||||
"ai.suggestion.list",
|
||||
"ai.prompt.list",
|
||||
"ai.usage.list",
|
||||
"message.list",
|
||||
"workflow.list",
|
||||
"workflow.read",
|
||||
];
|
||||
assign_perms(db, "doctor", &doctor_perms).await?;
|
||||
|
||||
// === 3. Nurse 角色:完整权限重新分配 ===
|
||||
let nurse_perms = vec![
|
||||
"health.patient.list",
|
||||
"health.patient.manage",
|
||||
"health.health-data.list",
|
||||
"health.follow-up.list",
|
||||
"health.follow-up.manage",
|
||||
"health.consultation.list",
|
||||
"health.action-inbox.list",
|
||||
"health.action-inbox.manage",
|
||||
"health.daily-monitoring.list",
|
||||
"health.daily-monitoring.manage",
|
||||
"health.alerts.list",
|
||||
"health.critical-alerts.list",
|
||||
"health.diagnosis.list",
|
||||
"health.consent.list",
|
||||
"health.consent.manage",
|
||||
"health.device-readings.list",
|
||||
"health.devices.list",
|
||||
"health.appointment.list",
|
||||
"health.appointment.manage",
|
||||
"message.list",
|
||||
];
|
||||
assign_perms(db, "nurse", &nurse_perms).await?;
|
||||
|
||||
// === 4. Operator 角色:完整权限重新分配 ===
|
||||
let operator_perms = vec![
|
||||
"health.patient.list",
|
||||
"health.tags.list",
|
||||
"health.tags.manage",
|
||||
"health.articles.list",
|
||||
"health.articles.manage",
|
||||
"health.articles.review",
|
||||
"health.points.list",
|
||||
"health.points.manage",
|
||||
"health.devices.list",
|
||||
"health.alerts.list",
|
||||
"health.dashboard.manage",
|
||||
"ai.usage.list",
|
||||
"message.list",
|
||||
];
|
||||
assign_perms(db, "operator", &operator_perms).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
// No down — this is a corrective migration
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//! 紧急修复:doctor 角色权限被误清,重新分配完整权限
|
||||
|
||||
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> {
|
||||
let db = manager.get_connection();
|
||||
|
||||
// 先清理 doctor 角色被软删除的记录
|
||||
db.execute_unprepared(
|
||||
"DELETE FROM role_permissions WHERE role_id IN (SELECT id FROM roles WHERE code = 'doctor') AND deleted_at IS NOT NULL"
|
||||
).await?;
|
||||
|
||||
let doctor_perms = vec![
|
||||
"health.patient.list",
|
||||
"health.patient.manage",
|
||||
"health.health-data.list",
|
||||
"health.follow-up.list",
|
||||
"health.follow-up.manage",
|
||||
"health.consultation.list",
|
||||
"health.consultation.manage",
|
||||
"health.doctor.list",
|
||||
"health.doctor.manage",
|
||||
"health.action-inbox.list",
|
||||
"health.action-inbox.manage",
|
||||
"health.daily-monitoring.list",
|
||||
"health.daily-monitoring.manage",
|
||||
"health.alerts.list",
|
||||
"health.alerts.manage",
|
||||
"health.alert-rules.list",
|
||||
"health.critical-alerts.list",
|
||||
"health.diagnosis.list",
|
||||
"health.diagnosis.manage",
|
||||
"health.consent.list",
|
||||
"health.consent.manage",
|
||||
"health.follow-up-templates.list",
|
||||
"health.follow-up-templates.manage",
|
||||
"health.appointment.list",
|
||||
"health.appointment.manage",
|
||||
"health.care-plan.list",
|
||||
"health.care-plan.manage",
|
||||
"health.dialysis.list",
|
||||
"health.dialysis.manage",
|
||||
"health.dialysis-prescription.list",
|
||||
"health.dialysis-prescription.manage",
|
||||
"health.dialysis.stats",
|
||||
"ai.analysis.list",
|
||||
"ai.suggestion.list",
|
||||
"ai.prompt.list",
|
||||
"ai.usage.list",
|
||||
"message.list",
|
||||
"workflow.list",
|
||||
"workflow.read",
|
||||
];
|
||||
|
||||
for code in &doctor_perms {
|
||||
db.execute(sea_orm::Statement::from_string(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
format!(
|
||||
"INSERT INTO role_permissions (role_id, permission_id, tenant_id, data_scope, created_at, updated_at, created_by, updated_by, deleted_at, version) \
|
||||
SELECT r.id, p.id, r.tenant_id, 'all', NOW(), NOW(), r.id, r.id, NULL, 1 \
|
||||
FROM roles r \
|
||||
JOIN permissions p ON p.tenant_id = r.tenant_id AND p.code = '{code}' AND p.deleted_at IS NULL \
|
||||
WHERE r.code = 'doctor' AND r.deleted_at IS NULL \
|
||||
ON CONFLICT (role_id, permission_id) DO UPDATE SET \
|
||||
data_scope = 'all', deleted_at = NULL, updated_at = NOW(), version = role_permissions.version + 1"
|
||||
),
|
||||
)).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user