/** * 二维码数据处理器 * 用于将扫描到的二维码数据转换为表单数据格式 */ import { QRCodeData, ValidationResult } from '@/types/qrcode'; import { validateQRCodeData, sanitizeData } from './qrCodeValidator'; import dayjs from 'dayjs'; /** * 从中国身份证号中提取性别 * @param idNumber 身份证号(18位) * @returns 'M' (男) | 'F' (女) | undefined (无法判断) */ const extractGenderFromIdNumber = (idNumber: string): 'M' | 'F' | undefined => { // 验证身份证号格式(18位) if (!idNumber || idNumber.length !== 18) { return undefined; } // 获取第17位(索引16)- 性别标识位 const genderDigit = parseInt(idNumber[16]); // 验证是否为有效数字 if (isNaN(genderDigit)) { return undefined; } // 奇数为男性,偶数为女性 return genderDigit % 2 === 1 ? 'M' : 'F'; }; /** * 解析制表符分隔的文本格式(姓名[TAB]年龄[TAB]岁[TAB]身份证号) * @param text 原始文本 * @returns 解析后的数据对象 */ const parseTabSeparatedText = (text: string) => { // 清理多余的空格和制表符,统一转换为单个空格 const cleaned = text.replace(/[\t\s]+/g, ' ').trim(); const parts = cleaned.split(/\s+/); console.log('[QRCodeParse] 解析文本:', text); console.log('[QRCodeParse] 清理后:', cleaned); console.log('[QRCodeParse] 分割结果:', parts); if (parts.length >= 4) { // 预期格式:[姓名, 年龄, 岁/年龄单位, 身份证号, ...] const name = parts[0]; // 姓名 const ageStr = parts[1]; // 年龄数字 const unitStr = parts[2]; // 年龄单位(如"岁") const idNumber = parts[3]; // 身份证号(第4个位置) // 验证年龄是否为数字 const ageNumber = parseInt(ageStr); if (isNaN(ageNumber)) { console.error('[QRCodeParse] 年龄格式错误:', ageStr); return null; } // 转换年龄单位 let ageUnit: 'D' | 'M' | 'Y' = 'Y'; // 默认年 if (unitStr === '岁' || unitStr === '年') { ageUnit = 'Y'; } else if (unitStr === '月') { ageUnit = 'M'; } else if (unitStr === '天' || unitStr === '日') { ageUnit = 'D'; } // 从身份证号提取性别 const gender = extractGenderFromIdNumber(idNumber); const patient_sex = gender || 'O'; // 如果无法判断,使用 'O' (Other) const result = { patient_name: name, patient_age: { number: ageNumber, unit: ageUnit }, patient_id: idNumber, patient_sex: patient_sex }; console.log('[QRCodeParse] 解析结果:', result); console.log('[QRCodeParse] 性别判断:', gender ? `从身份证号提取: ${gender}` : '无法判断性别,使用默认值 O'); return result; } console.error('[QRCodeParse] 文本格式不匹配,期望至少4个部分,实际:', parts.length); return null; }; /** * 解析二维码原始字符串 * @param rawData 原始字符串 * @returns 解析后的对象,如果解析失败返回 null */ export const parseQRCodeString = (rawData: string): any | null => { // 1. 首先尝试解析为 JSON try { const parsed = JSON.parse(rawData); console.log('[QRCodeParse] 成功解析为 JSON'); return parsed; } catch (error) { console.log('[QRCodeParse] 不是有效的 JSON,尝试其他格式'); } // 2. 尝试解析为制表符分隔的文本格式 const textResult = parseTabSeparatedText(rawData); if (textResult) { console.log('[QRCodeParse] 成功解析为文本格式'); return textResult; } console.error('[QRCodeParse] 无法解析二维码数据:', rawData); return null; }; /** * 处理二维码数据 * 包括解析、验证、消毒和转换 * @param rawData 原始二维码字符串 * @returns 处理结果 */ export const processQRCodeData = ( rawData: string ): { success: boolean; data?: QRCodeData; validation?: ValidationResult; error?: string; } => { // 1. 解析 JSON const parsed = parseQRCodeString(rawData); if (!parsed) { return { success: false, error: '二维码格式错误,请扫描有效的患者信息二维码' }; } // 2. 验证数据 const validation = validateQRCodeData(parsed); if (!validation.success) { return { success: false, validation, error: validation.errors.join(';') }; } // 3. 数据消毒 const sanitized = sanitizeData(parsed); // 4. 返回处理后的数据 return { success: true, data: sanitized as QRCodeData, validation }; }; /** * 将二维码数据转换为表单数据格式 * @param qrData 二维码数据 * @returns 表单数据 */ export const transformToFormData = (qrData: QRCodeData): any => { const formData: any = {}; // 基础字段直接复制 const directFields = [ 'patient_id', 'patient_name', 'patient_sex', 'accession_number', 'patient_size', 'weight', 'thickness', 'length', 'ref_physician', 'operator_id', 'comment', 'owner_name', 'variety', 'chip_number', 'sex_neutered', 'pregnancy_status' ]; directFields.forEach(field => { if (qrData[field as keyof QRCodeData] !== undefined) { formData[field] = qrData[field as keyof QRCodeData]; } }); // 年龄字段 - 已经是正确格式 if (qrData.patient_age) { formData.patient_age = qrData.patient_age; } else { // 如果没有年龄,设置默认值 formData.patient_age = { number: 0, unit: 'Y' }; } // 出生日期 - 转换为 dayjs 对象 if (qrData.patient_dob) { try { const dob = dayjs(qrData.patient_dob); if (dob.isValid()) { formData.patient_dob = dob; } } catch (error) { console.error('出生日期转换失败:', error); } } return formData; }; /** * 检查二维码是否包含体位信息 * @param qrData 二维码数据 * @returns 是否包含体位 */ export const hasViews = (qrData: QRCodeData): boolean => { return !!(qrData.views && qrData.views.length > 0); }; /** * 获取体位列表 * @param qrData 二维码数据 * @returns 体位列表 */ export const getViews = (qrData: QRCodeData): Array<{ view_id: string; procedure_id: string }> => { return qrData.views || []; };