# 二维码扫码录入病人信息功能 - 完整实现文档 ## 📋 功能概述 本功能允许用户通过扫描二维码快速录入病人信息。如果二维码中包含体位信息,系统将自动创建检查并进入检查页面。 ### 核心功能 - 📷 在注册页面打开摄像头扫描二维码 - 📝 自动填充病人信息到注册表单 - 🔄 如果携带体位信息,自动创建检查 - ▶️ 自动进入检查页面 --- ## 📊 需求详细分析 ### 1.1 核心功能需求 #### 1. 二维码扫描功能 - 在注册页面新增扫码按钮 - 点击后打开摄像头扫描界面 - 支持扫描二维码获取患者信息 #### 2. 自动填充表单 - 解析二维码中的患者信息 - 自动填充到注册表单的对应字段 - 验证数据格式和完整性 #### 3. 体位信息处理 - 检测二维码中是否包含体位信息 - 如果包含体位,自动选择对应的体位 #### 4. 自动创建检查并进入 - 如果携带体位信息,自动调用注册 API - 注册成功后自动切换到检查(exam)页面 - 如果不携带体位,仅填充表单,等待用户手动选择体位和注册 ### 1.2 二维码数据格式设计 ```typescript interface QRCodeData { // 基础患者信息(必填) patient_id: string; // 患者ID patient_name: string; // 患者姓名 patient_sex: string; // 性别 patient_age?: { // 年龄(可选) number: number; unit: 'D' | 'M' | 'Y'; }; patient_dob?: string; // 出生日期(可选) // 扩展信息(可选) accession_number?: string; // 检查号 patient_size?: string; // 患者尺寸 weight?: number; // 体重 thickness?: number; // 厚度 length?: number; // 身高 ref_physician?: string; // 医师 operator_id?: string; // 操作员 comment?: string; // 备注 // 宠物专用字段(可选) owner_name?: string; // 宠物主人 variety?: string; // 品种 chip_number?: string; // 芯片号 sex_neutered?: string; // 绝育状态 // 人医专用字段(可选) pregnancy_status?: string; // 妊娠状态 // 体位信息(可选,如果存在则自动进入检查) views?: Array<{ view_id: string; // 体位ID procedure_id: string; // 协议ID }>; } ``` --- ## 🏗️ 架构设计 ### 2.1 涉及的参与者 #### UI 组件层 1. **QRCodeScanButton**(新建) - 位置:注册页面右下角按钮区域 - 功能:触发二维码扫描 2. **QRCodeScanModal**(新建) - 摄像头视频流显示 - 扫描状态提示 - 扫描结果预览 - 取消/确认按钮 3. **BasicInfoForm**(修改) - 现有注册表单组件 - 需要支持外部数据填充 4. **RegisterPage**(修改) - 添加扫码按钮 - 集成扫码逻辑 #### 业务逻辑层 1. **QRCodeScanner**(新建) - 调用摄像头 - 解析二维码 - 返回解析结果 2. **QRCodeDataProcessor**(新建) - 验证二维码数据格式 - 转换数据到表单格式 - 处理体位信息 3. **AutoRegisterHandler**(新建) - 判断是否需要自动注册 - 执行自动注册流程 - 切换到检查页面 #### API 和服务层 1. **registerWork** (已存在) - 注册患者和检查 - 位置:`src/API/patient/workActions.ts` 2. **fetchViews** (已存在) - 获取可用体位列表 - 验证体位有效性 #### 状态管理层 1. **formSlice** (已存在) - 管理表单数据 - 位置:`src/states/patient/register/formSlice.ts` 2. **viewSelectionSlice** (需要确认) - 管理选中的体位 3. **qrCodeScanSlice**(新建) - 管理扫码状态 - 存储扫码结果 4. **BusinessFlowSlice** (已存在) - 管理业务流程切换 - 位置:`src/states/BusinessFlowSlice.ts` #### 工具库 1. **html5-qrcode** - 第三方二维码扫描库 - 需要通过 npm 安装 --- ## 📝 TodoList - 文件清单 ### 新建文件 - [ ] `src/components/QRCodeScanner/QRCodeScanButton.tsx` - 扫码触发按钮 - [ ] `src/components/QRCodeScanner/QRCodeScanModal.tsx` - 扫码模态框 - [ ] `src/components/QRCodeScanner/index.ts` - 导出文件 - [ ] `src/domain/qrcode/qrCodeDataProcessor.ts` - 二维码数据处理器 - [ ] `src/domain/qrcode/autoRegisterHandler.ts` - 自动注册处理器 - [ ] `src/domain/qrcode/qrCodeValidator.ts` - 二维码数据验证器 - [ ] `src/states/patient/register/qrCodeScanSlice.ts` - 扫码状态管理 - [ ] `src/types/qrcode.ts` - 二维码相关类型定义 - [ ] `src/hooks/useQRCodeScanner.ts` - 二维码扫描 Hook ### 修改文件 - [ ] `src/pages/patient/register.tsx` - 添加扫码按钮和逻辑 - [ ] `src/pages/patient/components/register.form.tsx` - 支持外部数据填充 - [ ] `src/states/store.ts` - 注册新的 qrCodeScanSlice - [ ] `package.json` - 添加二维码扫描库依赖 ### 可能需要修改的文件 - [ ] `src/states/patient/register/viewSelectionSlice.ts` - 添加批量选择体位的方法 - [ ] `src/validation/patient/registerSchema.ts` - 可能需要调整验证规则 --- ## 📊 交互流程 - 序列图 ```mermaid sequenceDiagram participant U as 用户 participant B as 扫码按钮 participant M as 扫码模态框 participant S as QRCodeScanner participant P as QRCodeDataProcessor participant F as 注册表单 participant A as AutoRegisterHandler participant API as registerWork API participant BF as BusinessFlow U->>B: 点击扫码按钮 B->>M: 打开模态框 M->>S: 启动摄像头 S->>S: 扫描二维码 S->>M: 返回二维码原始数据 M->>P: 传递数据进行处理 P->>P: 验证数据格式 P->>P: 转换为表单格式 alt 数据验证失败 P->>M: 返回错误信息 M->>U: 显示错误提示 else 数据验证成功 P->>F: 填充表单数据 M->>U: 关闭模态框 alt 包含体位信息 P->>A: 触发自动注册 A->>API: 调用注册接口 API->>A: 返回注册结果 alt 注册成功 A->>BF: 切换到检查页面 BF->>U: 显示检查页面 else 注册失败 A->>U: 显示错误提示 end else 不包含体位信息 F->>U: 显示填充后的表单 U->>U: 手动选择体位和注册 end end ``` --- ## 🔄 数据流图 ```mermaid flowchart TD A[二维码] --> B[QRCodeScanner] B --> C[原始字符串数据] C --> D[JSON.parse] D --> E[QRCodeData 对象] E --> F[QRCodeDataProcessor] F --> G{验证数据} G -->|失败| H[错误提示] G -->|成功| I[转换数据格式] I --> J[FormData] J --> K[填充表单] K --> L{是否包含体位?} L -->|否| M[等待用户操作] L -->|是| N[AutoRegisterHandler] N --> O[构建 RegisterInfo] O --> P[调用 registerWork API] P --> Q{注册成功?} Q -->|是| R[切换到 exam 页面] Q -->|否| S[显示错误] ``` --- ## 🗂️ 组件关系图 ```mermaid classDiagram class RegisterPage { +Form form +handleQRCodeScan() +handleRegister() } class QRCodeScanButton { +onClick() } class QRCodeScanModal { +visible: boolean +onScan(data) +onCancel() } class QRCodeScanner { +startScan() +stopScan() +onDetected(callback) } class QRCodeDataProcessor { +validate(data) +transform(data) +hasViews(data) } class AutoRegisterHandler { +execute(data) +selectViews(views) +callRegisterAPI() +navigateToExam() } class BasicInfoForm { +form: Form +setFieldsValue(data) } RegisterPage --> QRCodeScanButton RegisterPage --> QRCodeScanModal RegisterPage --> BasicInfoForm QRCodeScanModal --> QRCodeScanner QRCodeScanModal --> QRCodeDataProcessor QRCodeDataProcessor --> AutoRegisterHandler AutoRegisterHandler --> RegisterPage ``` --- ## 🔧 数据结构详细定义 ### QRCodeData(二维码数据格式) ```typescript /** * 二维码数据格式 * 用于定义二维码中包含的患者和检查信息 */ interface QRCodeData { // 基础患者信息(必填) patient_id: string; // 患者ID patient_name: string; // 患者姓名 patient_sex: string; // 性别: 'M', 'F', 'O' // 年龄信息(可选,如果提供则会覆盖 patient_dob) patient_age?: { number: number; // 数值 unit: 'D' | 'M' | 'Y'; // 单位:天/月/年 }; // 出生日期(可选,如果提供 patient_age 则优先使用 age) patient_dob?: string; // ISO 8601 格式 // 扩展信息(可选) accession_number?: string; // 检查号 patient_size?: string; // 患者尺寸: 'Small', 'Medium', 'Large' weight?: number; // 体重(kg) thickness?: number; // 厚度(cm) length?: number; // 身高(cm) ref_physician?: string; // 医师 operator_id?: string; // 操作员 comment?: string; // 备注 // 宠物专用字段(可选) owner_name?: string; // 宠物主人 variety?: string; // 品种 chip_number?: string; // 芯片号 sex_neutered?: 'ALTERED' | 'UNALTERED'; // 绝育状态 // 人医专用字段(可选) pregnancy_status?: 'NOT_PREGNANT' | 'POSSIBLY_PREGNANT' | 'DEFINITELY_PREGNANT' | 'UNKNOWN'; // 体位信息(可选,如果存在则自动进入检查) views?: Array<{ view_id: string; // 体位ID procedure_id: string; // 协议ID }>; } ``` ### QRCodeScanState(扫码状态) ```typescript /** * 二维码扫描状态 * 用于 Redux 状态管理 */ interface QRCodeScanState { isScanning: boolean; // 是否正在扫描 isProcessing: boolean; // 是否正在处理数据 error: string | null; // 错误信息 lastScanData: QRCodeData | null; // 最后扫描的数据 autoRegisterPending: boolean; // 是否等待自动注册 } ``` ### ValidationResult(验证结果) ```typescript /** * 数据验证结果 */ interface ValidationResult { success: boolean; // 验证是否成功 errors: string[]; // 错误列表 warnings: string[]; // 警告列表 } ``` --- ## 🚀 执行流程 ### 功能起点 **用户操作**:用户在注册页面点击"扫码录入"按钮 ### 完整执行流程 ```mermaid flowchart TD Start([用户点击扫码按钮]) --> A[打开 QRCodeScanModal] A --> B{请求摄像头权限} B -->|拒绝| C[显示权限提示] B -->|允许| D[启动摄像头] D --> E[实时扫描二维码] E --> F{检测到二维码?} F -->|否| E F -->|是| G[解析二维码内容] G --> H{JSON 格式有效?} H -->|否| I[显示格式错误] I --> J{用户选择} J -->|重试| E J -->|取消| End1([结束]) H -->|是| K[验证必填字段] K --> L{验证通过?} L -->|否| M[显示验证错误] M --> J L -->|是| N[转换数据格式] N --> O[填充表单] O --> P[关闭模态框] P --> Q{是否包含体位?} Q -->|否| R[等待用户手动操作] R --> End2([结束 - 表单已填充]) Q -->|是| S[选择体位] S --> T[调用注册 API] T --> U{注册成功?} U -->|否| V[显示注册错误] V --> End3([结束 - 保持在注册页]) U -->|是| W[切换到 exam 页面] W --> End4([结束 - 进入检查]) ``` ### 详细步骤说明 #### 1. 打开扫码界面 - 用户点击扫码按钮 - 打开 QRCodeScanModal 模态框 - 请求摄像头权限 - 启动摄像头视频流 #### 2. 扫描二维码 - 实时检测二维码 - 解析二维码内容(JSON 字符串) - 显示扫描成功提示 #### 3. 验证和处理数据 - 解析 JSON 数据为 QRCodeData 对象 - 验证必填字段(patient_id, patient_name, patient_sex) - 验证数据格式(年龄、日期等) - 转换为表单数据格式 #### 4. 填充表单 - 调用 form.setFieldsValue() 填充表单 - 更新 Redux formSlice 状态 - 关闭扫码模态框 #### 5. 判断是否自动注册 - 检查 QRCodeData.views 字段 - 如果存在体位信息,执行步骤 6 - 如果不存在,等待用户手动操作 #### 6. 自动注册流程(仅当包含体位时) - 验证体位 ID 的有效性 - 更新 viewSelectionSlice(选中体位) - 构建 RegisterInfo 对象 - 调用 registerWork API - 等待注册结果 #### 7. 进入检查页面(注册成功后) - 保存注册结果到缓存 - 调用 dispatch(setBusinessFlow('exam')) - 切换到检查页面 #### 8. 错误处理 - 摄像头权限拒绝:提示用户授权 - 二维码格式错误:提示重新扫描 - 数据验证失败:显示具体错误信息 - 注册失败:显示错误并保持在注册页面 --- ## 🧪 测试方案 ### 准备工作 #### 1. 生成测试二维码 使用以下 JSON 数据生成测试二维码(可以使用在线二维码生成器,如 [qr-code-generator.com](https://www.qr-code-generator.com/)) **示例 1:仅患者信息(不自动进入检查)** ```json { "patient_id": "P001", "patient_name": "张三", "patient_sex": "M", "patient_age": { "number": 30, "unit": "Y" }, "patient_size": "Medium", "weight": 70, "thickness": 25, "length": 175, "ref_physician": "李医生", "operator_id": "OP001", "comment": "常规检查" } ``` **示例 2:包含体位信息(自动进入检查)** ```json { "patient_id": "P002", "patient_name": "李四", "patient_sex": "F", "patient_age": { "number": 25, "unit": "Y" }, "patient_size": "Small", "weight": 55, "views": [ { "view_id": "view_001", "procedure_id": "proc_001" } ] } ``` **示例 3:宠物患者信息** ```json { "patient_id": "PET001", "patient_name": "旺财", "patient_sex": "M", "patient_age": { "number": 3, "unit": "Y" }, "owner_name": "王主人", "variety": "金毛", "chip_number": "CHI123456", "sex_neutered": "UNALTERED", "weight": 30, "thickness": 20 } ``` **示例 4:无效数据(测试错误处理)** ```json { "patient_name": "赵六", "patient_sex": "M" } ``` (缺少必填字段 patient_id) ### 测试场景 #### 场景 1:基础扫码填充表单 **测试目标**:验证扫码功能能够正确填充表单 **前置条件**: - 用户已登录系统 - 在注册页面 **操作步骤**: 1. 点击"扫码录入"按钮 2. 允许摄像头权限(如果首次使用) 3. 将测试二维码(示例 1)对准摄像头 4. 等待扫描成功 **预期结果**: - ✅ 摄像头正常启动并显示视频流 - ✅ 成功识别二维码 - ✅ 表单自动填充以下字段: - 患者ID: P001 - 患者姓名: 张三 - 性别: 男 - 年龄: 30岁 - 体重: 70kg - 厚度: 25cm - 身高: 175cm - 医师: 李医生 - 操作员: OP001 - 备注: 常规检查 - ✅ 扫码模态框自动关闭 - ✅ 用户可以继续选择体位 **实际结果**:_(测试时填写)_ **测试状态**:⬜ 通过 / ⬜ 失败 --- #### 场景 2:扫码后自动进入检查 **测试目标**:验证包含体位信息的二维码能够自动创建检查并进入检查页面 **前置条件**: - 用户已登录系统 - 在注册页面 - 系统中存在体位 view_001 和协议 proc_001 **操作步骤**: 1. 点击"扫码录入"按钮 2. 扫描包含体位信息的二维码(示例 2) 3. 观察系统行为 **预期结果**: - ✅ 表单自动填充患者信息 - ✅ 体位自动选中(view_001) - ✅ 自动调用注册接口 - ✅ 显示注册成功提示 - ✅ 自动跳转到检查(exam)页面 - ✅ 在检查页面能看到对应的体位 **实际结果**:_(测试时填写)_ **测试状态**:⬜ 通过 / ⬜ 失败 --- #### 场景 3:错误处理 - 无效二维码格式 **测试目标**:验证系统能够正确处理非 JSON 格式的二维码 **前置条件**: - 用户已登录系统 - 在注册页面 **操作步骤**: 1. 点击"扫码录入"按钮 2. 扫描一个纯文本二维码(如 "Hello World") **预期结果**: - ✅ 显示错误提示:"二维码格式错误,请扫描有效的患者信息二维码" - ✅ 不关闭扫码模态框 - ✅ 允许用户重新扫描 - ✅ 表单数据未被修改 **实际结果**:_(测试时填写)_ **测试状态**:⬜ 通过 / ⬜ 失败 --- #### 场景 4:错误处理 - 缺少必填字段 **测试目标**:验证系统能够检测并提示缺少必填字段 **前置条件**: - 用户已登录系统 - 在注册页面 **操作步骤**: 1. 点击"扫码录入"按钮 2. 扫描缺少 patient_id 的二维码(示例 4) **预期结果**: - ✅ 显示错误提示:"缺少必填字段:患者ID" - ✅ 不填充表单 - ✅ 不关闭扫码模态框 - ✅ 允许用户重新扫描 **实际结果**:_(测试时填写)_ **测试状态**:⬜ 通过 / ⬜ 失败 --- #### 场景 5:摄像头权限处理 **测试目标**:验证系统能够正确处理摄像头权限问题 **前置条件**: - 用户已登录系统 - 在注册页面 - 浏览器设置拒绝摄像头权限 **操作步骤**: 1. 在浏览器中拒绝摄像头权限 2. 点击"扫码录入"按钮 **预期结果**: - ✅ 显示权限提示:"需要摄像头权限才能使用扫码功能,请在浏览器设置中允许摄像头访问" - ✅ 提供浏览器权限设置指引 - ✅ 允许用户关闭模态框 **实际结果**:_(测试时填写)_ **测试状态**:⬜ 通过 / ⬜ 失败 --- #### 场景 6:宠物患者信息扫描 **测试目标**:验证宠物患者信息能够正确填充 **前置条件**: - 用户已登录系统(VETDROS 产品) - 在注册页面 **操作步骤**: 1. 点击"扫码录入"按钮 2. 扫描包含宠物信息的二维码(示例 3) **预期结果**: - ✅ 正确填充宠物专用字段: - 宠物主人: 王主人 - 品种: 金毛 - 芯片号: CHI123456 - 绝育状态: 未绝育 - ✅ 表单验证通过 **实际结果**:_(测试时填写)_ **测试状态**:⬜ 通过 / ⬜ 失败 --- #### 场景 7:年龄和出生日期联动 **测试目标**:验证扫码填充的年龄能够正确联动出生日期 **前置条件**: - 用户已登录系统 - 在注册页面 **操作步骤**: 1. 点击"扫码录入"按钮 2. 扫描包含年龄信息的二维码 3. 观察出生日期字段 **预期结果**: - ✅ 年龄字段正确填充 - ✅ 出生日期字段根据年龄自动计算并填充 - ✅ 年龄和出生日期保持联动关系 **实际结果**:_(测试时填写)_ **测试状态**:⬜ 通过 / ⬜ 失败 --- #### 场景 8:取消扫码操作 **测试目标**:验证用户可以随时取消扫码操作 **前置条件**: - 用户已登录系统 - 在注册页面 **操作步骤**: 1. 点击"扫码录入"按钮 2. 摄像头启动后,点击"取消"按钮 **预期结果**: - ✅ 摄像头停止工作 - ✅ 模态框关闭 - ✅ 表单数据未被修改 - ✅ 没有错误提示 **实际结果**:_(测试时填写)_ **测试状态**:⬜ 通过 / ⬜ 失败 --- #### 场景 9:注册失败后的处理 **测试目标**:验证自动注册失败后的错误处理 **前置条件**: - 用户已登录系统 - 在注册页面 - 后端 API 模拟注册失败 **操作步骤**: 1. 点击"扫码录入"按钮 2. 扫描包含体位的二维码 3. 观察注册失败后的行为 **预期结果**: - ✅ 显示注册失败错误提示 - ✅ 保持在注册页面 - ✅ 表单数据保留 - ✅ 体位选择保留 - ✅ 用户可以修改后重新注册 **实际结果**:_(测试时填写)_ **测试状态**:⬜ 通过 / ⬜ 失败 --- ### 回归测试 #### 回归测试 1:正常注册流程 **测试目标**:确保手动填写表单注册功能不受影响 **操作步骤**: 1. 手动填写表单所有字段 2. 选择体位 3. 点击注册按钮 **预期结果**: - ✅ 注册流程正常 - ✅ 数据正确提交 - ✅ 表单清空正常 **测试状态**:⬜ 通过 / ⬜ 失败 --- #### 回归测试 2:ReRegister 功能 **测试目标**:确保从历史记录预填充功能不受影响 **操作步骤**: 1. 从历史列表双击一条记录 2. 观察表单填充 **预期结果**: - ✅ 表单正确填充历史数据 - ✅ 功能正常工作 **测试状态**:⬜ 通过 / ⬜ 失败 --- #### 回归测试 3:急诊流程 **测试目标**:确保急诊快速注册功能不受影响 **操作步骤**: 1. 触发急诊流程 2. 观察自动注册和跳转 **预期结果**: - ✅ 急诊流程正常 - ✅ 自动进入检查页面 **测试状态**:⬜ 通过 / ⬜ 失败 --- ### 性能测试 #### 性能测试 1:扫码响应时间 **测试目标**:验证扫码识别的响应速度 **测试方法**: - 记录从二维码出现到识别成功的时间 - 测试 10 次取平均值 **预期结果**: - ⏱️ 平均识别时间 < 2 秒 - ⏱️ 最长识别时间 < 5 秒 **实际结果**:_(测试时填写)_ --- #### 性能测试 2:表单填充性能 **测试目标**:验证表单填充的流畅度 **测试方法**: - 测试填充包含所有字段的二维码数据 - 观察页面响应 **预期结果**: - ⚡ 填充操作无明显延迟 - ⚡ UI 无卡顿 **实际结果**:_(测试时填写)_ --- ### 兼容性测试 | 浏览器 | 版本 | 摄像头调用 | 二维码识别 | 表单填充 | 测试状态 | |--------|------|-----------|-----------|---------|---------| | Chrome | 最新 | ⬜ | ⬜ | ⬜ | ⬜ 通过 / ⬜ 失败 | | Edge | 最新 | ⬜ | ⬜ | ⬜ | ⬜ 通过 / ⬜ 失败 | | Firefox| 最新 | ⬜ | ⬜ | ⬜ | ⬜ 通过 / ⬜ 失败 | | Safari | 最新 | ⬜ | ⬜ | ⬜ | ⬜ 通过 / ⬜ 失败 | --- ## 🐛 潜在问题分析 ### 1. 摄像头权限问题 **问题描述**: - 用户可能拒绝摄像头权限 - 部分浏览器的权限提示不明确 - HTTPS 协议要求 **风险等级**:🔴 高 **影响范围**:扫码功能完全不可用 **解决方案**: 1. 提供清晰的权限请求说明 2. 捕获权限拒绝异常并给出友好提示 3. 提供浏览器设置指引 4. 确保应用运行在 HTTPS 环境(开发环境可用 localhost) **代码示例**: ```typescript try { const stream = await navigator.mediaDevices.getUserMedia({ video: true }); // 启动扫描 } catch (error) { if (error.name === 'NotAllowedError') { message.error('需要摄像头权限才能使用扫码功能,请允许访问摄像头'); } else if (error.name === 'NotFoundError') { message.error('未检测到摄像头设备'); } else { message.error('摄像头启动失败:' + error.message); } } ``` --- ### 2. 二维码识别准确性 **问题描述**: - 光线条件影响识别率 - 二维码模糊或损坏 - 识别速度慢 **风险等级**:🟡 中 **影响范围**:用户体验 **解决方案**: 1. 使用成熟的二维码识别库(html5-qrcode) 2. 提供识别状态反馈 3. 允许用户调整摄像头焦距 4. 提供重新扫描选项 **优化建议**: ```typescript // 配置二维码扫描器以提高识别率 const config = { fps: 10, // 每秒扫描帧数 qrbox: { width: 250, height: 250 }, // 扫描框大小 aspectRatio: 1.0, }; ``` --- ### 3. 数据格式不一致 **问题描述**: - 二维码中的数据格式与系统不匹配 - 字段名称不一致 - 数据类型错误 **风险等级**:🟡 中 **影响范围**:数据填充失败或错误 **解决方案**: 1. 严格的数据验证 2. 字段映射和转换 3. 提供详细的错误提示 4. 支持多种日期格式 **验证示例**: ```typescript const validateQRCodeData = (data: any): ValidationResult => { const errors: string[] = []; // 必填字段检查 if (!data.patient_id) errors.push('缺少患者ID'); if (!data.patient_name) errors.push('缺少患者姓名'); if (!data.patient_sex) errors.push('缺少性别信息'); // 格式检查 if (data.patient_sex && !['M', 'F', 'O'].includes(data.patient_sex)) { errors.push('性别格式错误'); } return { success: errors.length === 0, errors, warnings: [] }; }; ``` --- ### 4. 体位ID无效 **问题描述**: - 二维码中的体位ID在系统中不存在 - 体位已被禁用 - 体位不适用于当前患者类型 **风险等级**:🟡 中 **影响范围**:自动注册失败 **解决方案**: 1. 在自动注册前验证体位ID有效性 2. 调用 fetchViews API 检查体位是否存在 3. 如果验证失败,仅填充表单,不执行自动注册 4. 给出明确的错误提示 **验证流程**: ```typescript const validateViews = async (views: Array<{view_id: string, procedure_id: string}>): Promise => { try { for (const view of views) { const viewDetail = await fetchViewDetail(view.view_id); if (!viewDetail || !viewDetail.is_enabled) { message.warning(`体位 ${view.view_id} 不可用,已跳过自动注册`); return false; } } return true; } catch (error) { message.error('验证体位信息失败'); return false; } }; ``` --- ### 5. 网络请求失败 **问题描述**: - 自动注册时网络中断 - API 响应超时 - 服务器错误 **风险等级**:🔴 高 **影响范围**:自动注册流程中断 **解决方案**: 1. 完善的错误处理机制 2. 请求超时控制 3. 失败后保持表单数据,允许用户重试 4. 提供清晰的错误信息 **错误处理示例**: ```typescript const handleAutoRegister = async (data: QRCodeData) => { try { dispatch(setQRCodeProcessing(true)); const result = await registerWork(registerInfo); if (result.code === '0x000000') { message.success('注册成功,正在进入检查页面'); dispatch(setBusinessFlow('exam')); } else { message.error(`注册失败:${result.description}`); } } catch (error) { console.error('自动注册失败:', error); message.error('网络错误,注册失败,请检查网络连接后重试'); } finally { dispatch(setQRCodeProcessing(false)); } }; ``` --- ### 6. 并发问题 **问题描述**: - 用户在扫码过程中进行其他操作 - 多次快速扫码 - 扫码和手动填写冲突 **风险等级**:🟢 低 **影响范围**:数据覆盖或状态混乱 **解决方案**: 1. 扫码过程中禁用表单编辑 2. 处理数据时显示 loading 状态 3. 使用 debounce 防止重复扫描 4. 在填充前确认用户意图 **防抖示例**: ```typescript const debouncedScan = debounce((decodedText: string) => { handleQRCodeScanned(decodedText); }, 500); ``` --- ### 7. 浏览器兼容性 **问题描述**: - 部分浏览器不支持 getUserMedia API - 旧版本浏览器兼容性 - 移动端浏览器差异 **风险等级**:🟡 中 **影响范围**:部分用户无法使用功能 **解决方案**: 1. 功能检测,不支持时隐藏扫码按钮 2. 提供降级方案(手动输入) 3. 明确支持的浏览器版本 4. 在文档中说明系统要求 **功能检测**: ```typescript const isCameraSupported = (): boolean => { return !!( navigator.mediaDevices && navigator.mediaDevices.getUserMedia ); }; // 在组件中使用 {isCameraSupported() && } ``` --- ### 8. 性能问题 **问题描述**: - 视频流占用内存 - 持续扫描消耗 CPU - 模态框未正确释放资源 **风险等级**:🟢 低 **影响范围**:页面性能下降 **解决方案**: 1. 关闭模态框时停止摄像头 2. 限制扫描帧率 3. 及时清理资源 4. 使用 useEffect cleanup 函数 **资源清理**: ```typescript useEffect(() => { return () => { // 组件卸载时停止摄像头 if (scannerRef.current) { scannerRef.current.stop(); } // 释放媒体流 if (streamRef.current) { streamRef.current.getTracks().forEach(track => track.stop()); } }; }, []); ``` --- ### 9. 安全性问题 **问题描述**: - 恶意二维码注入 - XSS 攻击风险 - 敏感信息泄露 **风险等级**:🔴 高 **影响范围**:系统安全 **解决方案**: 1. 严格的数据验证和消毒 2. 限制允许的字段 3. 转义特殊字符 4. 不在二维码中包含敏感信息 5. 后端再次验证数据 **数据消毒**: ```typescript const sanitizeData = (data: any): QRCodeData => { // 只保留允许的字段 const allowedFields = [ 'patient_id', 'patient_name', 'patient_sex', // ... 其他允许的字段 ]; const sanitized: any = {}; allowedFields.forEach(field => { if (data[field] !== undefined) { // 转义特殊字符 sanitized[field] = typeof data[field] === 'string' ? escapeHtml(data[field]) : data[field]; } }); return sanitized as QRCodeData; }; ``` --- ### 10. 用户体验问题 **问题描述**: - 扫码界面不够直观 - 缺少扫描指引 - 错误提示不清晰 - 无法预览扫描结果 **风险等级**:🟡 中 **影响范围**:用户满意度 **解决方案**: 1. 提供清晰的扫描指引动画 2. 实时显示扫描框 3. 成功/失败有明确的视觉反馈 4. 允许用户预览和确认数据 5. 提供"取消"选项 **UX 优化建议**: - 添加扫描框高亮动画 - 成功时播放提示音(可选) - 失败时震动反馈(移动端) - 显示"将二维码对准扫描框"提示 - 提供手电筒开关(移动端) --- ## 📚 技术选型 ### 二维码扫描库对比 | 库名称 | Stars | 优点 | 缺点 | 推荐指数 | |--------|-------|------|------|---------| | html5-qrcode | 4.5k+ | 简单易用、文档完善、持续维护 | 相对较重 | ⭐⭐⭐⭐⭐ | | react-qr-scanner | 1k+ | React 友好 | 文档较少、维护不活跃 | ⭐⭐⭐ | | jsQR | 3k+ | 轻量级、纯 JS | 需要自己处理视频流 | ⭐⭐⭐⭐ | | qrcode-decoder | 500+ | 轻量 | 功能有限 | ⭐⭐⭐ | **最终选择**:**html5-qrcode** **理由**: 1. ✅ 活跃维护,文档完善 2. ✅ 支持多种扫描模式(摄像头、文件上传) 3. ✅ 良好的浏览器兼容性 4. ✅ TypeScript 支持 5. ✅ 易于集成到 React 项目 --- ## 🔐 安全考虑 ### 1. 数据验证 **服务端验证**: - 即使前端已验证,后端必须再次验证所有数据 - 检查数据类型、长度、格式 - 防止 SQL 注入、XSS 攻击 ### 2. 权限控制 **摄像头访问**: - 仅在需要时请求权限 - 用户可以随时撤销权限 - 不存储摄像头数据 ### 3. 数据隐私 **敏感信息**: - 不在二维码中包含密码、身份证号等敏感信息 - 仅包含注册必需的基本信息 - 遵守数据保护法规(GDPR、个人信息保护法) ### 4. 传输安全 **HTTPS**: - 强制使用 HTTPS 协议 - 防止中间人攻击 - 保护数据传输安全 --- ## 🚀 未来优化方向 ### 1. 功能增强 - [ ] 支持批量扫码(一次扫描多个患者) - [ ] 支持扫描条形码 - [ ] 支持从图片文件识别二维码 - [ ] 支持扫码历史记录 - [ ] 支持自定义二维码格式 ### 2. 用户体验优化 - [ ] 添加扫描成功动画 - [ ] 提供语音提示 - [ ] 支持暗光环境扫描 - [ ] 移动端优化(手电筒、自动对焦) - [ ] 提供扫码教程视频 ### 3. 性能优化 - [ ] 懒加载二维码扫描库 - [ ] 优化扫描算法 - [ ] 减少内存占用 - [ ] 支持 WebAssembly 加速 ### 4. 集成增强 - [ ] 与 RIS 系统集成 - [ ] 支持 HL7 FHIR 标准 - [ ] 与电子病历系统对接 - [ ] 支持多种二维码标准 --- ## 📖 参考文档 ### 内部文档 - [急诊流程实现文档](./急诊流程.md) - [注册表单实现文档](./注册表单实现.md) ### 外部资源 - [html5-qrcode 官方文档](https://github.com/mebjas/html5-qrcode) - [MDN - MediaDevices.getUserMedia()](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia) - [QR Code 规范](https://www.qrcode.com/en/about/standards.html) ### 技术标准 - [DICOM 标准](https://www.dicomstandard.org/) - [HL7 FHIR](https://www.hl7.org/fhir/) --- ## 📝 变更日志 | 版本 | 日期 | 作者 | 变更内容 | |------|------|------|---------| | 1.0.0 | 2025-12-19 | AI Assistant | 初始版本,完成功能设计和文档编写 | --- ## ✅ 总结 本文档详细设计了二维码扫码录入病人信息的完整功能,包括: 1. **需求分析**:明确了功能需求和二维码数据格式 2. **架构设计**:设计了组件结构和数据流 3. **实现计划**:列出了详细的文件清单和开发任务 4. **测试方案**:提供了全面的测试场景和步骤 5. **风险分析**:识别了潜在问题并提供了解决方案 6. **安全考虑**:强调了数据安全和隐私保护 该功能将极大提升患者注册的效率,特别是在批量患者登记和急诊场景下。通过二维码扫描,可以减少手动输入错误,加快工作流程,提高用户满意度。 --- **📌 下一步行动**: 1. 安装 html5-qrcode 依赖 2. 创建相关组件和工具函数 3. 实现核心功能 4. 进行单元测试和集成测试 5. 用户验收测试