| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- /**
- * 二维码数据处理器
- * 用于将扫描到的二维码数据转换为表单数据格式
- */
- 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 || [];
- };
|