退出检查的逻辑.md 19 KB

退出检查的逻辑

1. 概述

本文档详细说明了从检查页面(exam)退出的完整逻辑,包括曝光状态检查、用户反馈、后端通知等流程。

退出检查是一个关键的业务流程,需要根据当前的曝光状态做出不同的处理,以确保检查数据的完整性和用户操作的安全性。


2. 核心逻辑流程

2.1 触发退出的入口

当用户尝试从检查页面离开时(即当前业务流程是 exam,目标是其他流程),会触发退出逻辑。

关键判断函数:

function isExitingExam(currentAction: string, currentKey: string): boolean {
  return currentAction !== 'exam' && currentKey === 'exam';
}

位置: src/states/businessFlowMiddlewareLogic.ts

2.2 退出前的曝光状态检查

系统会检查当前的曝光状态(exposureStatus),根据不同状态采取不同的处理策略。


3. 曝光状态检查

3.1 三种曝光状态

状态 英文 说明 处理方式
未曝光 Not Exposed 所有体位都未曝光 直接允许退出
半曝光 Half Exposed 部分体位已曝光,部分未曝光 显示退出反馈弹窗
全曝光 Fully Exposed 所有体位都已曝光 通知后端,允许退出

3.2 半曝光状态的处理(核心)

当曝光状态为 "Half Exposed" 时:

  1. 显示退出反馈弹窗 ExamExitFeedback
  2. 阻止立即退出 - 不执行业务流程切换
  3. 提供3个选项供用户选择:

选项1:继续检查

  • 按钮文本: "继续检查"
  • 用户意图: 留在检查页面,继续完成未曝光的体位
  • 处理逻辑:

    dispatch(setFeedbackOpen(false)); // 关闭弹窗
    dispatch(setBusinessFlow('exitExamSuspended')); // 挂起检查
    

    选项2:保存并完成

    • 按钮文本: "保存并完成"
    • 按钮样式: type="primary"
    • 用户意图: 保存已曝光的体位,标记检查为完成状态
    • 处理逻辑: typescript dispatch(setFeedbackOpen(false)); // 关闭弹窗 dispatch(setBusinessFlow('exitExamCompleted')); // 完成检查

选项3:直接中止

  • 按钮文本: "直接中止"
  • 按钮样式: type="primary" danger
  • 用户意图: 不保存,直接中止检查
  • 处理逻辑:

    dispatch(setFeedbackOpen(false)); // 仅关闭弹窗
    // 注意:这里只关闭弹窗,不改变业务流程
    

    4. 退出处理的两种模式

    4.1 完成检查(exitExamCompleted)

    业务流程标识: 'exitExamCompleted'

    处理逻辑:

    if (action.payload === 'exitExamCompleted') {
    // 1. 获取当前检查的 StudyID
    const studyId = state.bodyPositionList.selectedBodyPosition.work.StudyID;
    
    // 2. 调用API通知后端 - 标记为已完成
    await suspendOrCompleteStudy(studyId, 'Completed');
    
    // 3. 跳转到目标业务流程
    return next({ ...action, payload: continueBusinessFlow });
    }
    

API参数:

  • study_id: 当前检查的ID
  • study_status: 'Completed' - 标记为已完成

4.2 挂起检查(exitExamSuspended)

业务流程标识: 'exitExamSuspended'

处理逻辑:

if (action.payload === 'exitExamSuspended') {
  // 1. 获取当前检查的 StudyID
  const studyId = state.bodyPositionList.selectedBodyPosition.work.StudyID;

  // 2. 调用API通知后端 - 标记为进行中
  await suspendOrCompleteStudy(studyId, 'InProgress');

  // 3. 跳转到目标业务流程
  return next({ ...action, payload: continueBusinessFlow });
}

API参数:

  • study_id: 当前检查的ID
  • study_status: 'InProgress' - 标记为进行中

4.3 目标流程的保存

全局变量: continueBusinessFlow

作用: 保存用户最初想要跳转的目标业务流程

示例场景:

  1. 用户在检查页面,点击"工作列表"按钮 → continueBusinessFlow = 'worklist'
  2. 系统检测到半曝光状态,显示弹窗 → 阻止跳转
  3. 用户选择"保存并完成" → 调用API后,跳转到 worklist

5. 特殊情况处理

5.1 从检查进入图像处理(process)

特殊性: 曝光操作可能导致自动进入图像处理页面

判断函数:

function isFromExamToView(currentAction: string, currentKey: string): boolean {
  return currentAction === 'process' && currentKey === 'exam';
}

处理逻辑:

if (isFromExamToView(action.payload, currentKey)) {
  // 检查发生器状态
  if (store.getState().generatorMonitor.acquisitionState === 1) {
    // 发生器正在采集 - 阻止退出
    console.log('发生器正在采集,不能退出');
    return next(action);
  } else {
    // 非曝光导致的退出 - 执行清理
    console.log('从检查退出到处理');
    unprepare(); // 清理检查相关资源
  }
}

关键状态: generatorMonitor.acquisitionState

  • 1: 正在采集 - 阻止退出
  • 其他值: 空闲状态 - 允许退出

6. 核心代码位置

6.1 主要文件

文件路径 作用 关键内容
src/states/businessFlowMiddlewareLogic.ts 业务流程中间件(核心) 所有退出判断和处理逻辑
src/pages/exam/components/ExamExitFeedback.tsx 退出反馈UI组件 "检查未完成"弹窗
src/pages/exam/LargeScreen.tsx 大屏幕组件 集成退出反馈弹窗
src/states/exam/largeScreenSlice.ts 大屏幕状态管理 管理反馈弹窗显示状态
src/states/BusinessFlowSlice.ts 业务流程状态管理 管理业务流程切换
src/API/patient/workActions.ts API接口 suspendOrCompleteStudy() 函数
src/domain/exam/prepare.ts 检查准备逻辑 prepare()unprepare() 函数

6.2 代码片段位置

退出判断逻辑

文件: src/states/businessFlowMiddlewareLogic.ts 行数: 约 200-250 行

if (
  isExitingExam(action.payload, currentKey) &&
  action.payload !== 'exitExamCompleted' &&
  action.payload !== 'exitExamSuspended' &&
  action.payload !== 'process'
) {
  const exposureStatus = store.getState().bodyPositionList.exposureStatus;

  if (exposureStatus === 'Half Exposed') {
    store.dispatch(setFeedbackOpen(true));
    continueBusinessFlow = action.payload;
    return; // 阻止退出
  }
}

退出反馈弹窗

文件: src/pages/exam/LargeScreen.tsx 行数: 约 20-30 行

<ExamExitFeedback
  open={isFeedbackOpen}
  onContinue={() => {
    dispatch(setFeedbackOpen(false));
    dispatch(setBusinessFlow('exitExamSuspended'));
  }}
  onSave={() => {
    dispatch(setFeedbackOpen(false));
    dispatch(setBusinessFlow('exitExamCompleted'));
  }}
  onAbort={() => dispatch(setFeedbackOpen(false))}
/>

7. 退出流程图

7.1 完整流程图

用户点击离开exam
    ↓
检查是否从exam退出?
    ↓ (是)
判断目标页面
    ├─ process (图像处理)
    │   ↓
    │   检查发生器状态
    │   ├─ acquisitionState === 1 → 阻止退出
    │   └─ 其他 → unprepare() → 允许退出
    │
    └─ 其他页面 (worklist, register, etc.)
        ↓
        检查曝光状态
        ├─ Not Exposed (未曝光)
        │   ↓
        │   直接允许退出
        │
        ├─ Half Exposed (半曝光)
        │   ↓
        │   显示退出反馈弹窗
        │   ↓
        │   用户选择
        │   ├─ 继续检查 → 关闭弹窗,停留在exam
        │   ├─ 保存并完成 → API调用(Completed) → 跳转到目标页面
        │   └─ 直接中止 → API调用(InProgress) → 跳转到目标页面
        │
        └─ Fully Exposed (全曝光)
            ↓
            通知后端 → 允许退出

7.2 状态转换图

[Exam Page]
    |
    | 用户触发退出
    ↓
[检查曝光状态]
    |
    ├─→ [Not Exposed] ──────────→ [直接退出]
    |
    ├─→ [Half Exposed] ──→ [显示弹窗]
    |                         |
    |                         ├─→ [继续检查] → [停留在Exam]
    |                         ├─→ [保存并完成] → [exitExamCompleted]
    |                         └─→ [直接中止] → [exitExamSuspended]
    |
    └─→ [Fully Exposed] ─────────→ [通知后端] → [退出]

8. 关键状态变量

8.1 Redux State

State路径 类型 说明
largeScreen.isFeedbackOpen boolean 退出反馈弹窗是否显示
bodyPositionList.exposureStatus string 曝光状态:'Not Exposed' / 'Half Exposed' / 'Fully Exposed'
bodyPositionList.selectedBodyPosition.work.StudyID string 当前检查的Study ID
generatorMonitor.acquisitionState number 发生器采集状态:1=采集中
BusinessFlow.currentKey string 当前业务流程

8.2 模块级变量

变量名 类型 作用 位置
continueBusinessFlow string 保存退出检查后要去的目标业务流程 businessFlowMiddlewareLogic.ts

9. API接口说明

9.1 suspendOrCompleteStudy

函数签名:

const suspendOrCompleteStudy = async (
  studyId: string,
  studyStatus: 'InProgress' | 'Completed'
): Promise<{ code: string; description: string; solution: string; data: {} }>

API端点:

POST /auth/task/inspection/leave

请求参数:

{
  "study_id": "string",
  "study_status": "InProgress" | "Completed"
}

响应示例:

{
  "code": "0x000000",
  "description": "Success",
  "solution": "",
  "data": {}
}

调用场景:

  1. 挂起检查: studyStatus = 'InProgress'
    • 用户选择"继续检查"后
    • 用户选择"直接中止"后
  2. 完成检查: studyStatus = 'Completed'
    • 用户选择"保存并完成"后

10. 测试场景

10.1 场景1:未曝光状态直接退出

前置条件:

  • 用户已登录系统
  • 从工作列表进入检查页面
  • 未进行任何曝光操作

测试步骤:

  1. 验证当前在检查页面(data-testid="exam-page"存在)
  2. 检查曝光状态为 "Not Exposed"
  3. 点击导航按钮切换到工作列表
  4. 验证成功退出到工作列表页面
  5. 验证未显示退出反馈弹窗

预期结果:

  • ✅ 直接退出检查页面
  • ✅ 未显示退出反馈弹窗
  • ✅ 成功进入工作列表页面

10.2 场景2:半曝光状态 - 继续检查

前置条件:

  • 用户已登录系统
  • 从工作列表进入检查页面
  • 已完成部分体位的曝光(至少1个体位已曝光,至少1个体位未曝光)

测试步骤:

  1. 验证当前在检查页面
  2. 检查曝光状态为 "Half Exposed"
  3. 点击导航按钮切换到工作列表
  4. 验证显示退出反馈弹窗
  5. 验证弹窗标题为"检查未完成"
  6. 验证弹窗内容包含提示信息
  7. 点击"继续检查"按钮
  8. 验证弹窗关闭
  9. 验证仍停留在检查页面

预期结果:

  • ✅ 显示退出反馈弹窗
  • ✅ 点击"继续检查"后弹窗关闭
  • ✅ 停留在检查页面,不跳转
  • ✅ Redux store中 isFeedbackOpen = false
  • ✅ 业务流程仍为 exam

10.3 场景3:半曝光状态 - 保存并完成

前置条件:

  • 用户已登录系统
  • 从工作列表进入检查页面
  • 已完成部分体位的曝光

测试步骤:

  1. 验证当前在检查页面
  2. 记录当前检查的 StudyID
  3. 检查曝光状态为 "Half Exposed"
  4. Mock API /auth/task/inspection/leave
  5. 点击导航按钮切换到工作列表
  6. 验证显示退出反馈弹窗
  7. 点击"保存并完成"按钮
  8. 验证弹窗关闭
  9. 验证调用了API,参数为:
    • study_id: 当前StudyID
    • study_status: "Completed"
  10. 验证成功跳转到工作列表页面

预期结果:

  • ✅ 显示退出反馈弹窗
  • ✅ 点击"保存并完成"后调用API
  • ✅ API参数正确(study_status='Completed')
  • ✅ 成功跳转到目标页面
  • ✅ 弹窗关闭

10.4 场景4:半曝光状态 - 直接中止

前置条件:

  • 用户已登录系统
  • 从工作列表进入检查页面
  • 已完成部分体位的曝光

测试步骤:

  1. 验证当前在检查页面
  2. 记录当前检查的 StudyID
  3. 检查曝光状态为 "Half Exposed"
  4. Mock API /auth/task/inspection/leave
  5. 点击导航按钮切换到工作列表
  6. 验证显示退出反馈弹窗
  7. 点击"直接中止"按钮(危险按钮)
  8. 验证弹窗关闭
  9. 验证调用了API,参数为:
    • study_id: 当前StudyID
    • study_status: "InProgress"
  10. 验证成功跳转到工作列表页面

预期结果:

  • ✅ 显示退出反馈弹窗
  • ✅ "直接中止"按钮为危险样式(红色)
  • ✅ 点击后调用API
  • ✅ API参数正确(study_status='InProgress')
  • ✅ 成功跳转到目标页面

10.5 场景5:全曝光状态退出

前置条件:

  • 用户已登录系统
  • 从工作列表进入检查页面
  • 已完成所有体位的曝光

测试步骤:

  1. 验证当前在检查页面
  2. 检查曝光状态为 "Fully Exposed"
  3. 点击导航按钮切换到工作列表
  4. 验证成功退出到工作列表页面
  5. 验证未显示退出反馈弹窗
  6. 验证后端收到通知(可选)

预期结果:

  • ✅ 直接退出检查页面
  • ✅ 未显示退出反馈弹窗
  • ✅ 成功进入工作列表页面
  • ✅ 后端收到完成通知(如果有)

10.6 场景6:发生器采集中阻止退出到process

前置条件:

  • 用户已登录系统
  • 在检查页面
  • 模拟发生器正在采集(acquisitionState = 1

测试步骤:

  1. 验证当前在检查页面
  2. 设置发生器状态为采集中
  3. 尝试跳转到图像处理页面(process)
  4. 验证跳转被阻止
  5. 验证仍停留在检查页面

预期结果:

  • ✅ 发生器采集中时,阻止退出到process
  • ✅ 停留在检查页面
  • ✅ 控制台输出相应日志

10.7 场景7:从检查正常退出到process(非采集)

前置条件:

  • 用户已登录系统
  • 在检查页面
  • 发生器空闲(acquisitionState != 1
  • 有已曝光的图像

测试步骤:

  1. 验证当前在检查页面
  2. 确认发生器不在采集状态
  3. 点击进入图像处理页面
  4. 验证成功跳转到process页面
  5. 验证调用了 unprepare() 清理函数

预期结果:

  • ✅ 成功退出到process页面
  • ✅ 执行了清理操作(unprepare)
  • ✅ 控制台输出相应日志

10.8 边界场景:快速连续点击退出

前置条件:

  • 用户已登录系统
  • 在检查页面(半曝光状态)

测试步骤:

  1. 快速连续点击退出按钮(3次)
  2. 验证只显示一个退出反馈弹窗
  3. 验证弹窗功能正常

预期结果:

  • ✅ 只显示一个弹窗
  • ✅ 不会出现多个弹窗叠加
  • ✅ 弹窗功能正常

10.9 边界场景:弹窗显示时切换曝光状态

前置条件:

  • 用户已登录系统
  • 在检查页面(半曝光状态)
  • 退出反馈弹窗已显示

测试步骤:

  1. 显示退出反馈弹窗
  2. 模拟后台完成剩余曝光(状态变为全曝光)
  3. 验证弹窗行为

预期结果:

  • 弹窗仍然显示(或根据业务需求自动关闭)
  • 状态保持一致

11. 相关Redux Actions和Reducers

11.1 Actions

Action 类型 Payload 说明
setBusinessFlow Action string 切换业务流程
setFeedbackOpen Action boolean 控制退出反馈弹窗显示

11.2 Selectors

推荐创建以下Selectors方便测试和使用:

// 选择曝光状态
const selectExposureStatus = (state: RootState) =>
  state.bodyPositionList.exposureStatus;

// 选择当前StudyID
const selectCurrentStudyId = (state: RootState) =>
  state.bodyPositionList.selectedBodyPosition?.work?.StudyID;

// 选择反馈弹窗状态
const selectIsFeedbackOpen = (state: RootState) =>
  state.largeScreen.isFeedbackOpen;

// 选择发生器状态
const selectAcquisitionState = (state: RootState) =>
  state.generatorMonitor.acquisitionState;

12. 注意事项和最佳实践

12.1 错误处理

  1. API调用失败:

    • 应该有重试机制
    • 向用户显示错误信息
    • 记录错误日志
  2. StudyID为空:

    • 在调用API前验证StudyID存在
    • 如果为空,记录错误并阻止退出

12.2 用户体验

  1. 按钮禁用状态:

    • API调用期间,按钮应该禁用
    • 显示Loading状态
  2. 清晰的提示信息:

    • 弹窗文本应该清楚说明当前状态
    • 按钮文本应该明确表达操作结果
  3. 操作确认:

    • "直接中止"是危险操作,使用危险样式提醒用户

12.3 性能优化

  1. 防抖处理:

    • 退出按钮点击应该有防抖,避免重复触发
  2. 状态检查优化:

    • 曝光状态应该实时更新
    • 避免过时的状态导致错误判断

13. 未来改进建议

13.1 功能增强

  1. 自动保存:

    • 定期自动保存检查进度
    • 减少数据丢失风险
  2. 退出历史:

    • 记录用户的退出操作
    • 用于审计和分析
  3. 智能提示:

    • 根据历史操作,智能建议用户操作

13.2 代码优化

  1. 状态机模式:

    • 使用状态机管理退出流程
    • 使逻辑更清晰
  2. 抽象公共逻辑:

    • 将退出逻辑抽象为独立函数
    • 提高可测试性和可维护性

14. 相关文档


更新记录

日期 修改人 修改内容
2025/10/7 - 创建文档,详细说明退出检查的逻辑