急诊注销后重新登录直接进入检查页面问题修复.md 9.0 KB

急诊注销后重新登录直接进入检查页面问题修复

问题描述

以急诊身份登录系统后,退出到登录页面,然后使用正常账号登录,会直接进入检查页面,跳过了注册页面。

问题现象

  1. 用户以急诊身份登录系统
  2. 进入检查页面(exam)
  3. 注销退出到登录页面
  4. 使用正常账号重新登录
  5. 系统直接进入检查页面,跳过了注册页面

根本原因分析

急诊登录时设置的状态

src/domain/patient/handleEmergencyOperation.ts 中,急诊登录时会设置多个状态:

// Step 2: Set system mode to Emergency
dispatch(setSystemMode(SystemMode.Emergency));

// Step 3: Set temporary user info with guest token
dispatch(setUserInfo({
  token: guestToken,
  expire: Date.now() + 24 * 60 * 60 * 1000,
  uid: -1, // Special uid to identify emergency mode
  name: 'Emergency User',
  avatar: '',
}));

// Step 6: Save registration result to cache
const task = mapToTask(registrationResult.data);
dispatch(addWork(task)); // ← 添加急诊工单到缓存

// Step 7: Proceed to Examination
dispatch(setBusinessFlow('exam')); // ← 设置业务流程为检查页面

注销时只清除了部分状态

在修复前,src/components/ExitModal.tsx 的注销逻辑只清除了:

case 'logout':
  actionName = '注销用户';
  dispatch(clearUserInfo());                    // ✅ 清除用户信息
  dispatch(setSystemMode(SystemMode.Normal));   // ✅ 重置系统模式
  message.success('已退出登录');
  onClose();
  return;

关键问题:注销时没有重置以下状态

  • BusinessFlow.currentKey - 仍然是 'exam'
  • examWorksCache.works - 仍包含急诊工单数据
  • bodyPositionList.bodyPositions - 仍包含急诊体位数据
  • bodyPositionList.selectedBodyPosition - 仍有选中的体位

问题的原因链

注销操作
  ↓
只清除 userInfo 和 systemMode
  ↓
BusinessFlow.currentKey 仍然是 'exam'
examWorksCache.works 仍包含急诊患者数据
bodyPositionList 仍包含急诊体位数据
  ↓
用户重新登录(普通账号)
  ↓
系统检测到 BusinessFlow.currentKey === 'exam'
  ↓
直接渲染检查页面
  ↓
用户看到上一个急诊患者的数据!(数据泄露)

各个 Slice 的初始状态

BusinessFlowSlice

const initialState: BusinessFlowState = {
  currentKey: 'register', // ← 默认应该是注册页面
  lastKey: '',
  shouldKeepSelection: false,
  keptSelectionSopUid: undefined,
};

examWorksCacheSlice

const initialState: ExamWorksCacheState = {
  works: [], // ← 默认是空数组
  loading: false,
};

bodyPositionListSlice

const initialState: BodyPositionListState = {
  bodyPositions: [], // ← 默认是空数组
  selectedBodyPosition: null, // ← 默认是 null
  exposureStatus: null,
  loading: false,
  error: null,
};

安全隐患

数据泄露风险

如果不清理以下状态,会导致严重的患者隐私泄露:

  1. examWorksCache.works - 包含患者信息:

    • PatientName(患者姓名)
    • PatientID(患者ID)
    • AccessionNumber(登记号)
    • StudyDescription(检查描述)
    • Views(检查视图/体位信息)
  2. bodyPositionList.bodyPositions - 包含详细的检查数据:

    • 患者基本信息
    • 检查图像数据
    • 体位配置参数
    • DICOM 实例 UID

业务逻辑问题

  1. 新用户登录后直接进入检查页面,跳过注册流程
  2. 新用户可能看到并误操作其他患者的数据
  3. 系统状态不一致,可能导致其他功能异常

解决方案

方案对比

方案 A:使用现有 action(已采用)

优点:

  • ✅ 不需要修改 slice 代码
  • ✅ 使用现有 action,改动最小
  • ✅ 功能完全满足需求

缺点:

  • ⚠️ 需要调用多个 dispatch

方案 B:添加专用清理 action

优点:

  • ✅ 更优雅,一次调用清理所有状态
  • ✅ 可以添加日志追踪

缺点:

  • ❌ 需要修改多个 slice
  • ❌ 改动较大

最终采用方案 A

src/components/ExitModal.tsx 中完整清理所有相关状态:

case 'logout':
  actionName = '注销用户';
  // 应用级注销:清除所有用户相关状态
  
  // 1. 清除用户信息
  dispatch(clearUserInfo());
  
  // 2. 重置系统模式,避免急诊模式注销后黑屏
  dispatch(setSystemMode(SystemMode.Normal));
  
  // 3. 重置业务流程到注册页面,避免直接进入检查页面
  dispatch(setBusinessFlow('register'));
  
  // 4. 清理工单缓存,避免患者数据泄露
  dispatch(clearWorks());
  
  // 5. 清理体位列表,避免患者数据泄露
  dispatch(setBodyPositions([]));
  dispatch(setSelectedBodyPosition(null));
  
  message.success('已退出登录');
  onClose();
  return;

修改文件

src/components/ExitModal.tsx

新增导入

import { setBusinessFlow } from '../states/BusinessFlowSlice';
import { clearWorks } from '../states/exam/examWorksCacheSlice';
import {
  setBodyPositions,
  setSelectedBodyPosition,
} from '../states/exam/bodyPositionListSlice';

修改注销逻辑:添加了3个额外的状态清理操作。

需要清理的完整状态列表

序号 State 清理方法 原因
1 userInfo clearUserInfo() 清除用户登录信息
2 systemMode setSystemMode(SystemMode.Normal) 重置系统模式,避免黑屏
3 BusinessFlow setBusinessFlow('register') 重置到注册页面,避免直接进入检查
4 examWorksCache clearWorks() 清理工单缓存,避免患者数据泄露
5 bodyPositionList setBodyPositions([]) + setSelectedBodyPosition(null) 清理体位数据,避免患者数据泄露

测试验证

测试步骤

  1. 启动应用
  2. 点击"急诊"按钮以急诊身份登录
  3. 验证进入检查页面,能看到急诊患者信息
  4. 点击退出按钮
  5. 在退出 Modal 中点击"注销用户"
  6. 点击确认
  7. 验证返回登录页面
  8. 使用正常账号登录(如 admin/123456)

预期结果

  • ✅ 显示"已退出登录"消息
  • ✅ Modal 关闭
  • ✅ 返回登录页面
  • ✅ 重新登录后进入注册页面(不是检查页面)
  • 看不到上一个急诊患者的任何数据
  • ✅ 系统完全重置到初始状态

状态验证

注销后应确保:

  • userInfo.token = ''
  • userInfo.uid = 0
  • systemMode = 'Normal'
  • BusinessFlow.currentKey = 'register'
  • examWorksCache.works = []
  • bodyPositionList.bodyPositions = []
  • bodyPositionList.selectedBodyPosition = null
  • loggedIn = false

影响范围

修改的文件

  • src/components/ExitModal.tsx

影响的功能

  • ✅ 急诊注销流程 - 重要改进
  • ✅ 普通用户注销流程 - 不受影响
  • ✅ 其他退出方式(关闭程序、关机)- 不受影响

解决的问题

  1. ✅ 注销后重新登录直接进入检查页面 - 已修复
  2. ✅ 患者数据泄露风险 - 已消除
  3. ✅ 系统状态不一致 - 已修复

注意事项

  1. 数据安全

    • 此修复消除了严重的患者隐私泄露风险
    • 确保注销后所有患者相关数据被清除
    • 符合医疗信息安全要求
  2. 业务逻辑

    • 注销后系统完全重置到初始状态
    • 新用户登录时从注册页面开始,符合正常流程
    • 不会看到或误操作其他患者的数据
  3. 与已有功能的兼容性

    • 此修改与现有的注销逻辑完全兼容
    • 不会影响普通用户的注销体验
    • 不会影响关闭程序和关机功能
  4. 状态管理最佳实践

    • 使用现有 action 进行状态清理
    • 代码改动最小,降低引入新 bug 的风险
    • 注释清晰,便于维护

相关文档

完成状态

  • 问题分析
  • 安全隐患评估
  • 解决方案设计
  • 代码修改
  • 测试验证(待手动测试)

后续优化建议

  1. 添加 Cypress 测试

    • 创建急诊注销并重新登录的端到端测试
    • 验证所有状态都被正确清理
    • 验证无法访问上一个用户的数据
  2. 考虑添加统一的清理 action

    • 在未来可以考虑在各个 slice 中添加 resetclear action
    • 提供一个统一的清理接口
    • 便于在其他场景下重用
  3. 添加状态监控

    • 在开发环境下监控状态清理是否完整
    • 记录清理操作的日志
    • 便于调试和问题追踪
  4. 安全审计

    • 定期审查是否有其他地方需要清理敏感数据
    • 确保所有用户切换场景都正确清理数据
    • 符合医疗数据保护法规要求