const { ObjectId } = require("mongodb"); const validator = require("../../utils/validator"); const Sm4Util = require("../../utils/sm4-util"); const hnHis = require("../hn-his"); const COLLECTION_NAME = "hlw-patient-self-auth"; const PATIENT_COLLECTION_NAME = "hlw-patient"; const AUTH_VALID_MS = 10 * 60 * 1000; const RECORD_RETENTION_MS = 24 * 60 * 60 * 1000; const indexPromises = new WeakMap(); async function removeLegacyAuthIdIndex(collection) { try { await collection.dropIndex("authId_1"); } catch (error) { // 旧索引不存在时无需处理;其他数据库错误仍应暴露,避免留下隐蔽的唯一索引冲突。 if (error && (error.code === 27 || error.codeName === "IndexNotFound")) return; throw error; } } function ensureIndexes(db) { if (!indexPromises.has(db)) { const collection = db.collection(COLLECTION_NAME); const promise = removeLegacyAuthIdIndex(collection) .then(() => Promise.all([ collection.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 }), collection.createIndex({ ownerUserId: 1, status: 1 }), ])) .catch((error) => { indexPromises.delete(db); throw error; }); indexPromises.set(db, promise); } return indexPromises.get(db); } function parseRequestId(value) { const id = typeof value === "string" ? value.trim() : ""; if (!/^[a-f\d]{24}$/i.test(id) || !ObjectId.isValid(id)) { return { success: false, message: "授权链接无效" }; } return { success: true, id, objectId: new ObjectId(id), }; } function isExpired(requestTimestamp, now = Date.now()) { return now - requestTimestamp > AUTH_VALID_MS; } function getCurrentUserId(context) { const internalUserId = context && typeof context.userId === "string" ? context.userId.trim() : ""; return ObjectId.isValid(internalUserId) ? internalUserId : ""; } function decryptPatient(patient) { return { id: patient._id.toString(), accountId: patient.accountId, name: Sm4Util.decryptDataForSm3(patient.anotherName), certNo: Sm4Util.decryptDataForSm3(patient.anotherIdNo), mobile: Sm4Util.decryptDataForSm3(patient.anotherMobile), address: typeof patient.address === "string" ? patient.address : "", }; } function maskCertNo(certNo) { if (typeof certNo !== "string" || certNo.length <= 7) return "--"; return `${certNo.slice(0, 4)}${"*".repeat(certNo.length - 7)}${certNo.slice(-3)}`; } function getPersonInfo(certNo, now = new Date()) { if (!validator.isChinaId(certNo)) return { age: "", sex: "" }; const birthYear = Number(certNo.slice(6, 10)); const birthMonth = Number(certNo.slice(10, 12)); const birthDay = Number(certNo.slice(12, 14)); const birthday = new Date(birthYear, birthMonth - 1, birthDay); if ( birthday.getFullYear() !== birthYear || birthday.getMonth() + 1 !== birthMonth || birthday.getDate() !== birthDay || birthday > now ) { return { age: "", sex: "" }; } let age = now.getFullYear() - birthYear; const birthdayPassed = now.getMonth() + 1 > birthMonth || (now.getMonth() + 1 === birthMonth && now.getDate() >= birthDay); if (!birthdayPassed) age -= 1; return { age: Math.max(0, age), sex: Number(certNo.charAt(16)) % 2 === 0 ? "女" : "男", }; } async function findRequest(db, parsed) { return db.collection(COLLECTION_NAME).findOne({ _id: parsed.objectId }); } async function createSelfAuthRequest(item, db, context) { const patientId = item && typeof item.patientId === "string" ? item.patientId.trim() : ""; if (!ObjectId.isValid(patientId)) { return { success: false, message: "就诊人ID格式不正确" }; } try { await ensureIndexes(db); const ownerUserId = getCurrentUserId(context); const accountId = item && typeof item.accountId === "string" ? item.accountId.trim() : ""; if (!ownerUserId || !accountId) return { success: false, message: "登录信息无效,请重新登录" }; const patient = await db.collection(PATIENT_COLLECTION_NAME).findOne({ _id: new ObjectId(patientId), accountId, disabled: false, }); if (!patient) return { success: false, message: "就诊人不存在或无权操作" }; const requestTimestamp = Date.now(); const insertResult = await db.collection(COLLECTION_NAME).insertOne({ patientId: new ObjectId(patientId), ownerAccountId: accountId, ownerUserId, requestTimestamp, status: "pending", attempts: 0, createdAt: new Date(requestTimestamp), updatedAt: new Date(requestTimestamp), expiresAt: new Date(requestTimestamp + RECORD_RETENTION_MS), }); return { success: true, message: "授权请求创建成功", data: { id: insertResult.insertedId.toString() }, }; } catch (error) { return { success: false, message: error.message || "授权请求创建失败" }; } } async function getSelfAuthPatient(item, db) { const parsed = parseRequestId(item && item.id); if (!parsed.success) return parsed; try { await ensureIndexes(db); const request = await findRequest(db, parsed); if (!request) return { success: false, message: "授权链接不存在或已失效" }; if (request.status !== "success" && isExpired(request.requestTimestamp)) { return { success: false, message: "授权链接已过期,请重新发起授权" }; } const patient = await db.collection(PATIENT_COLLECTION_NAME).findOne({ _id: request.patientId, accountId: request.ownerAccountId, disabled: false, }); if (!patient) return { success: false, message: "就诊人不存在" }; const data = decryptPatient(patient); const personInfo = getPersonInfo(data.certNo); return { success: true, message: "获取就诊人成功", data: { name: data.name, certNo: maskCertNo(data.certNo), age: personInfo.age, sex: personInfo.sex, status: request.status, }, }; } catch (error) { return { success: false, message: error.message || "获取授权信息失败" }; } } async function submitSelfAuth(item, db, context) { const parsed = parseRequestId(item && item.id); if (!parsed.success) return parsed; const psnToken = item && typeof item.psnToken === "string" ? item.psnToken.trim() : ""; if (!psnToken || psnToken.length > 2048) { return { success: false, message: "医保授权信息无效" }; } const collection = db.collection(COLLECTION_NAME); try { await ensureIndexes(db); const request = await findRequest(db, parsed); if (!request) return { success: false, message: "授权链接不存在或已失效" }; if (request.status === "success") { return { success: true, message: "已完成授权", data: { status: "success" } }; } if (isExpired(request.requestTimestamp)) { return { success: false, message: "授权链接已过期,请重新发起授权" }; } const authorizerUserId = getCurrentUserId(context); if (!authorizerUserId) return { success: false, message: "登录信息无效,请重新登录" }; const claimed = await collection.findOneAndUpdate( { _id: parsed.objectId, status: { $in: ["pending", "failed"] } }, { $set: { status: "processing", authorizerUserId, errorMessage: "", updatedAt: new Date(), }, $inc: { attempts: 1 }, }, { returnDocument: "after" } ); if (!claimed) { const latest = await collection.findOne({ _id: parsed.objectId }); if (latest && latest.status === "success") { return { success: true, message: "已完成授权", data: { status: "success" } }; } return { success: false, message: "授权正在处理中,请勿重复提交", data: { status: "processing" } }; } const patientRecord = await db.collection(PATIENT_COLLECTION_NAME).findOne({ _id: claimed.patientId, accountId: claimed.ownerAccountId, disabled: false, }); if (!patientRecord) throw new Error("就诊人不存在"); const patient = decryptPatient(patientRecord); const hisResult = await hnHis({ type: "addHisCustomer", idCard: patient.certNo, name: patient.name, mobile: patient.mobile, address: patient.address, psnToken, }); const patients = Array.isArray(hisResult && hisResult.list) ? hisResult.list : []; const hisArchive = patients.find((entry) => entry && entry.socialno === patient.certNo); if (!hisResult || !hisResult.success || !hisArchive) { throw new Error((hisResult && hisResult.message) || "医保建档失败,未找到匹配档案"); } if (hisArchive.isyb !== "1") throw new Error("医保建档失败"); const result = { ...hisArchive, socialno: hisArchive.socialno || patient.certNo, tmbxx: Array.isArray(hisResult.tmbxx) ? hisResult.tmbxx : [], }; const completedAt = new Date(); await collection.updateOne( { _id: parsed.objectId, status: "processing" }, { $set: { status: "success", encryptedResult: Sm4Util.encryptDataForSm3(JSON.stringify(result)), encryption: "SM4", completedAt, updatedAt: completedAt, }, $unset: { errorMessage: "" }, } ); return { success: true, message: "授权并建档成功", data: { status: "success" } }; } catch (error) { await collection.updateOne( { _id: parsed.objectId, status: "processing" }, { $set: { status: "failed", errorMessage: error.message || "医保建档失败", updatedAt: new Date() } } ).catch(() => { }); return { success: false, message: error.message || "医保建档失败", data: { status: "failed" } }; } } async function getSelfAuthResult(item, db, context) { const parsed = parseRequestId(item && item.id); console.log('parsed: ', parsed) if (!parsed.success) return parsed; try { await ensureIndexes(db); const ownerUserId = getCurrentUserId(context); if (!ownerUserId) return { success: false, message: "登录信息无效,请重新登录" }; const request = await findRequest(db, parsed); console.log('request: ', request) if (!request || request.ownerUserId !== ownerUserId) { return { success: false, message: "授权记录不存在或无权访问" }; } if (request.status === "success") { if (!request.encryptedResult || request.encryption !== "SM4") { return { success: false, message: "授权结果无效" }; } const hisArchive = JSON.parse(Sm4Util.decryptDataForSm3(request.encryptedResult)); return { success: true, message: "授权成功", data: { status: "success", hisArchive } }; } if (request.status !== "processing" && isExpired(request.requestTimestamp)) { return { success: true, message: "授权链接已过期", data: { status: "expired" } }; } return { success: true, message: request.errorMessage || "等待就诊人授权", data: { status: request.status, errorMessage: request.errorMessage || "" }, }; } catch (error) { return { success: false, message: error.message || "查询授权结果失败" }; } } module.exports = async (item, db, context) => { switch (item.type) { case "createSelfAuthRequest": return createSelfAuthRequest(item, db, context); case "getSelfAuthPatient": return getSelfAuthPatient(item, db); case "submitSelfAuth": return submitSelfAuth(item, db, context); case "getSelfAuthResult": return getSelfAuthResult(item, db, context); default: return { success: false, message: "未找到本人授权接口" }; } }; module.exports.parseRequestId = parseRequestId; module.exports.maskCertNo = maskCertNo; module.exports.getPersonInfo = getPersonInfo; module.exports.constants = { AUTH_VALID_MS, RECORD_RETENTION_MS, COLLECTION_NAME };