|
|
@@ -5,6 +5,7 @@
|
|
|
|
|
|
import { QRCodeData, ValidationResult } from '@/types/qrcode';
|
|
|
import { validateQRCodeData, sanitizeData } from './qrCodeValidator';
|
|
|
+import { generateRandomPatientId } from '@/domain/patient/patientIdGenerator';
|
|
|
import dayjs from 'dayjs';
|
|
|
|
|
|
/**
|
|
|
@@ -31,7 +32,146 @@ const extractGenderFromIdNumber = (idNumber: string): 'M' | 'F' | undefined => {
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
- * 解析制表符分隔的文本格式(姓名[TAB]年龄[TAB]岁[TAB]身份证号)
|
|
|
+ * 将性别文字转换为标准代码
|
|
|
+ * @param genderText 性别文字(男/女/M/F等)
|
|
|
+ * @returns 'M' | 'F' | 'O'
|
|
|
+ */
|
|
|
+const convertGenderText = (genderText: string): 'M' | 'F' | 'O' => {
|
|
|
+ const normalized = genderText.trim().toUpperCase();
|
|
|
+
|
|
|
+ if (normalized === '男' || normalized === 'M' || normalized === 'MALE') {
|
|
|
+ return 'M';
|
|
|
+ }
|
|
|
+ if (normalized === '女' || normalized === 'F' || normalized === 'FEMALE') {
|
|
|
+ return 'F';
|
|
|
+ }
|
|
|
+
|
|
|
+ return 'O'; // 其他情况返回 'O' (Other)
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 格式解析器:姓名-年龄-单位-身份证号
|
|
|
+ * 格式:姓名 年龄 岁 身份证号
|
|
|
+ * 例如:吴玉花 68岁 122410560115085053
|
|
|
+ * @param parts 分割后的文本数组
|
|
|
+ * @returns 解析后的数据对象,失败返回 null
|
|
|
+ */
|
|
|
+const parseFormat_NameAgeUnitIdCard = (parts: string[]) => {
|
|
|
+ // 验证格式:至少需要4个部分
|
|
|
+ if (parts.length < 4) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ const name = parts[0];
|
|
|
+ const ageStr = parts[1];
|
|
|
+ const unitStr = parts[2];
|
|
|
+ const idNumber = parts[3];
|
|
|
+
|
|
|
+ // 验证年龄单位是否为预期的单位词
|
|
|
+ const validUnits = ['岁', '年', '月', '天', '日'];
|
|
|
+ if (!validUnits.includes(unitStr)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证年龄是否为数字
|
|
|
+ const ageNumber = parseInt(ageStr);
|
|
|
+ if (isNaN(ageNumber)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证身份证号格式(18位数字)
|
|
|
+ if (idNumber.length !== 18 || !/^\d{18}$/.test(idNumber)) {
|
|
|
+ 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';
|
|
|
+
|
|
|
+ // 从身份证提取出生日期
|
|
|
+ const birthYear = parseInt(idNumber.substring(6, 10));
|
|
|
+ const birthMonth = parseInt(idNumber.substring(10, 12));
|
|
|
+ const birthDay = parseInt(idNumber.substring(12, 14));
|
|
|
+ const patient_dob = dayjs().year(birthYear).month(birthMonth - 1).date(birthDay);
|
|
|
+
|
|
|
+ console.log('[QRCodeParse] 使用格式:NameAgeUnitIdCard');
|
|
|
+ return {
|
|
|
+ patient_name: name,
|
|
|
+ patient_age: {
|
|
|
+ number: ageNumber,
|
|
|
+ unit: ageUnit
|
|
|
+ },
|
|
|
+ patient_id: idNumber,
|
|
|
+ patient_sex: patient_sex,
|
|
|
+ patient_dob: patient_dob.isValid() ? patient_dob.toISOString() : undefined
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 格式解析器:姓名-ID-年龄-性别
|
|
|
+ * 格式:姓名 ID号码 年龄 性别
|
|
|
+ * 例如:吴玉花 122410560115085053 68 女
|
|
|
+ * @param parts 分割后的文本数组
|
|
|
+ * @returns 解析后的数据对象,失败返回 null
|
|
|
+ */
|
|
|
+const parseFormat_NameIdAgeGender = (parts: string[]) => {
|
|
|
+ // 验证格式:至少需要4个部分
|
|
|
+ if (parts.length < 4) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ const name = parts[0];
|
|
|
+ const idNumber = parts[1];
|
|
|
+ const ageStr = parts[2];
|
|
|
+ const genderText = parts[3];
|
|
|
+
|
|
|
+ // 验证年龄是否为数字
|
|
|
+ const ageNumber = parseInt(ageStr);
|
|
|
+ if (isNaN(ageNumber)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证性别文字是否为有效的性别标识
|
|
|
+ const validGenders = ['男', '女','M', 'F', 'O', 'MALE', 'FEMALE'];
|
|
|
+ if (!validGenders.some(g => genderText.trim().toUpperCase() === g.toUpperCase())) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 转换性别
|
|
|
+ const patient_sex = convertGenderText(genderText);
|
|
|
+
|
|
|
+ // 生成随机患者ID
|
|
|
+ const patient_id = generateRandomPatientId();
|
|
|
+
|
|
|
+ console.log('[QRCodeParse] 使用格式: NameIdAgeGender');
|
|
|
+ console.log('[QRCodeParse] 生成随机患者ID:', patient_id);
|
|
|
+ console.log('[QRCodeParse] 登记号:', idNumber);
|
|
|
+
|
|
|
+ return {
|
|
|
+ patient_name: name,
|
|
|
+ patient_age: {
|
|
|
+ number: ageNumber,
|
|
|
+ unit: 'Y' as const // 默认单位为"岁"
|
|
|
+ },
|
|
|
+ patient_id: idNumber, // 扫码获取的一个编号填充patient_id
|
|
|
+ patient_sex: patient_sex,
|
|
|
+ patient_dob: undefined // 新格式不包含出生日期信息
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 解析制表符分隔的文本格式
|
|
|
+ * 支持多种格式,自动检测并使用合适的解析器
|
|
|
* @param text 原始文本
|
|
|
* @returns 解析后的数据对象
|
|
|
*/
|
|
|
@@ -44,57 +184,23 @@ const parseTabSeparatedText = (text: string) => {
|
|
|
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;
|
|
|
- }
|
|
|
+ // 定义解析器列表,按优先级顺序尝试
|
|
|
+ const parsers = [
|
|
|
+ parseFormat_NameIdAgeGender, // 新格式:姓名 ID 年龄 性别
|
|
|
+ parseFormat_NameAgeUnitIdCard, // 旧格式:姓名 年龄 单位 身份证号
|
|
|
+ //未来可以在这里添加更多格式解析器
|
|
|
+ ];
|
|
|
|
|
|
- // 转换年龄单位
|
|
|
- let ageUnit: 'D' | 'M' | 'Y' = 'Y'; // 默认年
|
|
|
- if (unitStr === '岁' || unitStr === '年') {
|
|
|
- ageUnit = 'Y';
|
|
|
- } else if (unitStr === '月') {
|
|
|
- ageUnit = 'M';
|
|
|
- } else if (unitStr === '天' || unitStr === '日') {
|
|
|
- ageUnit = 'D';
|
|
|
+ // 依次尝试每个解析器
|
|
|
+ for (const parser of parsers) {
|
|
|
+ const result = parser(parts);
|
|
|
+ if (result) {
|
|
|
+ console.log('[QRCodeParse] 解析成功,结果:', result);
|
|
|
+ return result;
|
|
|
}
|
|
|
-
|
|
|
- // 从身份证号提取性别
|
|
|
- const gender = extractGenderFromIdNumber(idNumber);
|
|
|
- const patient_sex = gender || 'O'; // 如果无法判断,使用 'O' (Other)
|
|
|
-
|
|
|
-
|
|
|
- // 从身份证提取出生日期
|
|
|
- const birthYear = parseInt(idNumber.substring(6, 10));
|
|
|
- const birthMonth = parseInt(idNumber.substring(10, 12));
|
|
|
- const birthDay = parseInt(idNumber.substring(12, 14));
|
|
|
- const patient_dob = dayjs().year(birthYear).month(birthMonth - 1).date(birthDay);
|
|
|
-
|
|
|
- const result = {
|
|
|
- patient_name: name,
|
|
|
- patient_age: {
|
|
|
- number: ageNumber,
|
|
|
- unit: ageUnit
|
|
|
- },
|
|
|
- patient_id: idNumber,
|
|
|
- patient_sex: patient_sex,
|
|
|
- patient_dob: patient_dob.isValid() ? patient_dob.toISOString() : undefined
|
|
|
- };
|
|
|
- console.log('[QRCodeParse] 解析结果:', result);
|
|
|
- console.log('[QRCodeParse] 性别判断:', gender ? `从身份证号提取: ${gender}` : '无法判断性别,使用默认值 O');
|
|
|
- return result;
|
|
|
}
|
|
|
|
|
|
- console.error('[QRCodeParse] 文本格式不匹配,期望至少4个部分,实际:', parts.length);
|
|
|
+ console.error('[QRCodeParse] 所有格式解析器均失败,无法识别文本格式');
|
|
|
return null;
|
|
|
};
|
|
|
|