# 急诊注销后重新登录直接进入检查页面问题修复 ## 问题描述 以急诊身份登录系统后,退出到登录页面,然后使用正常账号登录,会直接进入检查页面,跳过了注册页面。 ## 问题现象 1. 用户以急诊身份登录系统 2. 进入检查页面(exam) 3. 注销退出到登录页面 4. 使用正常账号重新登录 5. **系统直接进入检查页面,跳过了注册页面** ## 根本原因分析 ### 急诊登录时设置的状态 在 `src/domain/patient/handleEmergencyOperation.ts` 中,急诊登录时会设置多个状态: ```typescript // 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` 的注销逻辑只清除了: ```typescript 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 ```typescript const initialState: BusinessFlowState = { currentKey: 'register', // ← 默认应该是注册页面 lastKey: '', shouldKeepSelection: false, keptSelectionSopUid: undefined, }; ``` #### examWorksCacheSlice ```typescript const initialState: ExamWorksCacheState = { works: [], // ← 默认是空数组 loading: false, }; ``` #### bodyPositionListSlice ```typescript 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` 中完整清理所有相关状态: ```typescript 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 **新增导入**: ```typescript 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 的风险 - 注释清晰,便于维护 ## 相关文档 - [急诊退出黑屏问题修复](./急诊退出黑屏问题修复.md) - 前一个问题的修复 - [注销功能实现总结](../logout-implementation-summary.md) - 注销功能整体设计 - [急诊流程](./急诊流程.md) - 急诊功能设计 ## 完成状态 - [x] 问题分析 - [x] 安全隐患评估 - [x] 解决方案设计 - [x] 代码修改 - [ ] 测试验证(待手动测试) ## 后续优化建议 1. **添加 Cypress 测试**: - 创建急诊注销并重新登录的端到端测试 - 验证所有状态都被正确清理 - 验证无法访问上一个用户的数据 2. **考虑添加统一的清理 action**: - 在未来可以考虑在各个 slice 中添加 `reset` 或 `clear` action - 提供一个统一的清理接口 - 便于在其他场景下重用 3. **添加状态监控**: - 在开发环境下监控状态清理是否完整 - 记录清理操作的日志 - 便于调试和问题追踪 4. **安全审计**: - 定期审查是否有其他地方需要清理敏感数据 - 确保所有用户切换场景都正确清理数据 - 符合医疗数据保护法规要求