let db = ""; const { ObjectId } = require("mongodb"); const { getDatabase } = require("../../mongodb"); const dayjs = require("dayjs"); const { decryptOrderList } = require("../consult-order/format"); const { formatUploadRxData, formatOnlineMedicinePurchaseData, formatReviewData, formatWinePurchaseData, encryptDiagnosticRecordFields, decryptDiagnosticRecordFields, decryptDiagnosticRecordList, buildHybridSearchQuery, } = require("./format"); const hnHis = require("../hn-his"); const consultOrder = require("../consult-order"); const tencentIM = require("../tencent-im"); const beijingCa = require("../beijing-ca"); const BigNumber = require("bignumber.js"); // const timeApi = require("../hlw-time-duration"); const types = require("./types"); /** * 性能优化建议: * * 1. 数据库索引建议: * - diagnostic-record 集合: * db.getCollection("diagnostic-record").createIndex({ "status": 1, "createTime": -1 }) * db.getCollection("diagnostic-record").createIndex({ "orderId": 1 }) * db.getCollection("diagnostic-record").createIndex({ "doctorCode": 1, "status": 1 }) * db.getCollection("diagnostic-record").createIndex({ "patientId": 1, "status": 1, "orderSource": 1 }) * * - consult-order 集合: * db.getCollection("consult-order").createIndex({ "orderId": 1 }) * db.getCollection("consult-order").createIndex({ "orderStatus": 1 }) * * 2. 查询优化: * - getAuditDiagnosisList 函数已优化为先分页后关联查询,避免大数据量 $lookup * - 使用 Promise.all 并行执行不相关的查询操作 * - 避免在查询条件中使用正则表达式,考虑使用全文检索 * * 3. 数据冗余建议(可选): * - 在 diagnostic-record 中冗余 consult-order 的关键字段(如患者姓名、手机号等) * - 减少跨集合查询的需求 */ // 判断当前是否是 zytDev 环境 function isZytDevEnv() { console.log("当前环境变量 CONFIG_NODE_ENV:", process.env.CONFIG_NODE_ENV); return ( process.env.CONFIG_NODE_ENV === "zytDev" || process.env.CONFIG_NODE_ENV === "development" ); } // 根据环境决定是否调用 beijingCa async function conditionalCallBeijingCa(params, dbParam) { if (isZytDevEnv()) { console.log("在 zytDev 环境下,跳过调用 beijingCa"); return { success: true, userId: params.code || params.userId || "mock_user_id", message: "在 zytDev 环境下模拟返回", }; } return await beijingCa(params, dbParam); } const status = { init: "INIT", //待审核 pass: "PASS", //审核通过 unpass: "UNPASS", //审核未通过 expired: "EXPIRED", //订单过期 无法 passInvalid: "PASS_INVALID", //通过的处方作废 (目前就订单退费会导致处方作废) unpassInvalid: "UNPASS_INVALID", //未通过的处方作废 (目前就订单退费会导致处方作废) invalid: "INVALID", //处方作废 (目前就订单退费会导致处方作废) }; module.exports = async (item, mongodb, user) => { db = mongodb; switch (item.type) { case "addConsultDiagnosis": return await addConsultDiagnosis(item, db); case "getOrderDiagnosis": return await getOrderDiagnosis(item, db); case "getDiagnosisById": return await getDiagnosisById(item, db); case "getStoreDiagnosisRecord": return await getStoreDiagnosisRecord(item, db, user); case "markStoreRxPrinted": return await markStoreRxPrinted(item, db, user); case "getAuditDiagnosisList": return await getAuditDiagnosisList(item, db); case "getAuditedDiagnosisList": return await getAuditedDiagnosisList(item, db); case "getPatientDiagnosisRecord": return await getPatientDiagnosisRecord(item, db); case "getPatientPassDiagnosisRecord": return await getPatientPassDiagnosisRecord(item, db); case "auditDiagnosis": return await auditDiagnosis(item, db); case "uploadDiagnosis": return await uploadDiagnosis(item, db); case "orderHasDiagnosis": return await orderHasDiagnosis(item, db); case "getLatestSubmitTime": return await getLatestSubmitTime(item, db); case "reUploadDiagnosticRecord": return await reUploadDiagnosticRecord(item, db); case "discardDiagnosticRecord": return await discardDiagnosticRecord(item, db); case "getAllDiagnosisRecord": return await getAllDiagnosisRecord(item, db); case "getDoctorRxStats": return await getDoctorRxStats(item, db); case "getPharmacistRxStats": return await getPharmacistRxStats(item, db); case "checkRxUploadStatus": return await checkRxUploadStatus(item, db); case "getAccountRxList": return await getAccountRxList(item, db); case "getRxDetail": return await getRxDetail(item, db); case "getAccountHistoryDrugs": return await getAccountHistoryDrugs(item, db); case "getPatientHistoryDrugs": return await getPatientHistoryDrugs(item, db); case "getRxMedicineOrderStatus": return await getRxMedicineOrderStatus(item, db); case "runOnlineRxExpireSchedule": return await runOnlineRxExpireSchedule(db); case "getPatientHomeRxCount": return await getPatientHomeRxCount(item, db); case "getReUploadRxList": return await getReUploadRxList(item, db); case "handleReUploadRx": return await handleReUploadRx(item, db); case "getPatientWineBuyCount": return await getPatientWineBuyCount(item, db); case "getYbReviewResult": return await getYbReviewResult(item, db); } }; let scheduleTaskIsRunning = false; async function scheduleAutoPassAfterPrescription({ rxId, orderId, corpId, createTime, expireTime, }) { try { const automation = require("../automation"); await automation.scheduleAutoPassRx({ db, rxId, orderId, corpId, baseTime: createTime || Date.now(), expiresAt: expireTime, }); } catch (error) { console.error("[自动化任务] 创建自动审方任务失败:", error.message); } } async function addConsultDiagnosis(item) { let { params = {} } = item; const automated = item.automated === true; const operationSource = item.operationSource === "AUTO" ? "AUTO" : "MANUAL"; const corpId = typeof item.corpId === "string" ? item.corpId.trim() : ''; const { orderId, doctorCode } = params; if ( typeof orderId !== "string" || orderId.trim() === "" || typeof doctorCode !== "string" || doctorCode.trim() === "" ) { return { success: false, message: "参数错误" }; } const timeData = { type: 'addConsultDiagnosis', startTime: dayjs().format('YYYY-MM-DD HH:mm:ss'), orderId } try { const order = await db.collection("consult-order").findOne( { orderId, corpId }, { projection: { corpId: 1, createTime: 1, orderStatus: 1, registerId: 1, medorg_order_no: 1, accountId: 1, prescriptionStartTime: 1, expireTime: 1, areaCode: 1, pickUpType: 1, medInfo: 1, feeType: 1, sex: 1, age: 1 }, } ); timeData.orderQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); if (!order) { return { success: false, message: "订单不存在" }; } if (order.orderStatus !== "processing") { return { success: false, message: "当前订单状态不支持开方" }; } const orderExpired = Number.isFinite(order.expireTime) ? order.expireTime <= Date.now() : !(order.createTime > dayjs().startOf("day").valueOf()); if (orderExpired) { return { success: false, message: "当前订单不在有效期内" }; } // timeApi({ type: "addDoctorRxDuration", orderId }, db); const doctor = await db.collection("hlw-doctor").findOne({ corpId, doctorNo: doctorCode }); if (!doctor) { return { success: false, message: "医生不存在" }; } // 判断是否医生发的消息是否大于2条 // const chatCount = automated // ? 3 // : await db.collection("im-chat-msg").countDocuments({ // From_Account: doctorCode, // To_Account: orderId, // }); // timeData.chatCountQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); // if (!automated && chatCount < 3) { // return { // success: false, // message: "请先向患者发送2条提问消息后再开方", // }; // } const record = await db .collection("diagnostic-record") .findOne( { orderId, doctorCode }, { projection: { _id: 1, status: 1, pharmacistNo: 1, createTime: 1, expireTime: 1, }, } ); timeData.recordQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); if (automated && record && [status.init, status.pass].includes(record.status)) { await scheduleAutoPassAfterPrescription({ rxId: record._id, orderId, corpId, createTime: record.createTime, expireTime: record.expireTime || order.expireTime, }); return { success: true, message: "处方已存在", data: record._id, }; } if (record && record.status === status.init) { return { success: false, message: "该订单已存在诊断信息,请勿重复添加" }; } if (record && record.status === status.pass) { return { success: false, message: "该订单已审核通过,请勿重复添加" }; } if (record && record.status === status.unpass) { const rewriteResult = await rewriteConsultDiagnosis( params, record._id, corpId, record.pharmacistNo, timeData, order, operationSource ); if (rewriteResult && rewriteResult.success) { await scheduleAutoPassAfterPrescription({ rxId: record._id, orderId, corpId, createTime: Date.now(), expireTime: order.expireTime, }); } return rewriteResult; } // 获取推荐药师 随机获取药师 const { success, message, data: pharmacist, } = await getRecommendPharmacist({ corpId }); timeData.pharmacistQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); if (!success) { return { success: false, message }; } const data = { ...params, createTime: Date.now(), status: status.init, pharmacistNo: pharmacist.pharmacistNo, pharmacist: pharmacist.pharmacistName, doctorSignImg: doctor.signatureUrl, sex: order.sex, age: order.age, // prescriptionType: PrescriptionTypeMap[order.consultType], accountId: order.accountId, medOrgOrderNo: order.registerId || order.medorg_order_no, areaCode: order.areaCode, med_type: order.medInfo.med_type, dise_codg: order.medInfo.dise_codg, dise_name: order.medInfo.dise_name, feeType: order.feeType, corpId, prescriptionSource: operationSource, }; // 由后端统一设置处方时间: // - 开始时间从 consult-order 读取(接单时已由后端写入) // - 结束时间为首次提交医嘱的服务器时间 const hlwConfig = await db.collection('hlw-config').findOne({ corpId }); // 首次开方间隔时间(和接诊时间的时间间隔 单位秒) const firstSubmitRxIntervel = hlwConfig && Number.isInteger(hlwConfig.firstSubmitRxIntervel) ? hlwConfig.firstSubmitRxIntervel : 0; // 医生提交处方时间间隔 const submitRxInterval = hlwConfig && Number.isInteger(hlwConfig.submitRxInterval) ? hlwConfig.submitRxInterval : 0; if (typeof order.prescriptionStartTime !== "undefined") { data.prescriptionStartTime = order.prescriptionStartTime; } data.prescriptionEndTime = Date.now(); if (firstSubmitRxIntervel > 0 && (data.prescriptionEndTime - data.prescriptionStartTime) <= (firstSubmitRxIntervel * 1000)) { return { success: false, message: `接诊后${firstSubmitRxIntervel}秒内不允许提交处方, 请和患者充分沟通后再开方` } } if (!data.medOrgOrderNo) { data.medOrgOrderNo = order.registerId || order.medorg_order_no; } if (!data.accountId && order.accountId) { data.accountId = order.accountId; } if (submitRxInterval > 0) { const lastSubmitRecord = await db.collection('doctor-last-submit-time').findOne({ doctorCode: data.doctorCode }); const submitTime = lastSubmitRecord && lastSubmitRecord.submitTime > 0 ? lastSubmitRecord.submitTime : 0; const diff = Math.ceil((Date.now() - submitTime) / 1000); const interval = Math.ceil(submitRxInterval - diff); if (interval > 0) { return { success: false, message: `提交处方太频繁,请${interval}秒后再试` } } if (lastSubmitRecord) { await db.collection('doctor-last-submit-time').updateOne({ doctorCode: data.doctorCode }, { $set: { submitTime: Date.now() } }); } else { await db.collection('doctor-last-submit-time').insertOne({ doctorCode: data.doctorCode, submitTime: Date.now() }); } } // return { success: false, message: '不让提交', data } // 加密敏感字段 const encryptedData = encryptDiagnosticRecordFields(data); const { insertedId } = await db .collection("diagnostic-record") .insertOne(encryptedData); timeData.insertRecordTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); timeData.rxId = insertedId; // 发起咨询后, 咨询订单状态变为已完成 await updateOrderStatusAndSendNotification({ orderId, corpId, doctorCode, notification: `处方已开具,审方中.....请稍等`, syncOtherMachine: 1, orderStatus: "completed", msgType: "MEDICALADVICE", }, timeData); await scheduleAutoPassAfterPrescription({ rxId: insertedId, orderId, corpId, createTime: data.createTime, expireTime: data.expireTime || order.expireTime, }); timeData.duration = dayjs().diff(timeData.startTime, 's'); if (timeData.duration > 3) { db.collection("perf-observation").insertOne(timeData); } return { success: true, message: "新增成功", data: insertedId, }; } catch (err) { return { success: false, message: err.message || "新增失败", }; } } // 未通过的处方 重新提交 原药师在线的话 async function rewriteConsultDiagnosis( item, _id, corpId, pharmacistNo, timeData, order, operationSource = "MANUAL" ) { const { complaint, presentIllness, dispose, diagnosisList, drugs, orderId, doctorCode, } = item; try { timeData.type = 'rewriteConsultDiagnosis'; const data = { complaint, presentIllness, dispose, diagnosisList, drugs, wines: item.wines, updateTime: Date.now(), status: status.init, prescriptionSource: operationSource, }; const doctorType = order.consultType === 'wineConsult' ? 'chinese' : 'west' const { success, message, data: pharmacist, } = await getRecommendPharmacist({ pharmacistNo, type: doctorType }); timeData.pharmacistQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); if (!success) { return { success: false, message }; } // 如果药师不在线, 则重新获取药师 doctorNo doctorName if (pharmacist && pharmacist.doctorNo !== pharmacistNo) { data.pharmacistNo = pharmacist.doctorNo; data.pharmacist = pharmacist.doctorName; } // 加密敏感字段(如果有) const encryptedData = encryptDiagnosticRecordFields(data); const { modifiedCount } = await db .collection("diagnostic-record") .updateOne({ _id }, { $set: encryptedData }); await updateOrderStatusAndSendNotification({ orderId, corpId, doctorCode, notification: "医生重新提交诊断, 等待审方中", syncOtherMachine: 2, orderStatus: "completed", msgType: "REPEATMEDICALADVICE", }, timeData); timeData.rewriteStatus = modifiedCount === 1 ? 'success' : 'fail'; timeData.duration = dayjs().diff(timeData.startTime, 's'); if (timeData.duration > 3) { db.collection("perf-observation").insertOne(timeData); } if (modifiedCount === 1) { return { success: true, message: "更新成功" }; } return { success: false, message: "更新失败" }; } catch (e) { return { success: false, message: e.message || "更新失败" }; } } async function getDiagnosisById(ctx, db) { try { const _id = ObjectId.isValid(ctx.id) ? new ObjectId(ctx.id) : null; if (!_id) { return { success: false, message: "参数错误" }; } const data = await db.collection("diagnostic-record").findOne({ _id }); if (!data) { return { success: false, message: "查询失败" }; } // 解密敏感字段 const decryptedData = decryptDiagnosticRecordFields(data); return { success: true, message: "查询成功", data: decryptedData }; } catch (e) { return { success: false, message: e?.message } } } async function getOrderDiagnosis(item, db) { const { orderId, doctorNo: doctorCode, status } = item; if (typeof orderId !== "string" || orderId.trim() === "") { return { success: false, message: "参数错误" }; } try { // 兼容仅按 orderId 查询的场景(如从购药订单反查处方) const query = { orderId }; if (typeof doctorCode === "string" && doctorCode.trim() !== "") { query.doctorCode = doctorCode.trim(); } if (typeof status === "string" && status.trim() !== "") { query.status = status; } const data = await db.collection("diagnostic-record").findOne(query); // 解密敏感字段 const decryptedData = decryptDiagnosticRecordFields(data); return { success: true, message: "查询成功", data: decryptedData }; } catch (e) { return { success: false, message: e.message || "查询失败" }; } } async function resolveStoreIds(item, db, user) { let authorizedStoreId = user && typeof user.drugStoreId === "string" ? user.drugStoreId.trim() : ""; if (!authorizedStoreId && user && ObjectId.isValid(user.userId)) { const corpDb = await getDatabase("corp"); const account = await corpDb.collection("corp-member").findOne( { _id: new ObjectId(user.userId) }, { projection: { drugStoreId: 1 } } ); authorizedStoreId = account && typeof account.drugStoreId === "string" ? account.drugStoreId.trim() : ""; } if (!authorizedStoreId) { return []; } const storeIds = [authorizedStoreId]; const store = await db.collection("store-list").findOne( { store_id: authorizedStoreId }, { projection: { login_no: 1 } } ); if (store && typeof store.login_no === "string" && store.login_no.trim()) { storeIds.push(store.login_no.trim()); } return [...new Set(storeIds)]; } async function getStoreDiagnosisRecord(item, db, user) { const storeIds = await resolveStoreIds(item, db, user); if (storeIds.length === 0) { return { success: false, message: "参数错误" }; } const query = { drugStoreNo: { $in: [...new Set(storeIds)] }, status: status.pass, }; const page = item.page > 0 && Number.isInteger(item.page) ? item.page : 1; const pageSize = item.pageSize > 0 && Number.isInteger(item.pageSize) ? item.pageSize : 20; const startDate = item.startDate && dayjs(item.startDate).isValid() ? dayjs(item.startDate) : null; const endDate = item.endDate && dayjs(item.endDate).isValid() ? dayjs(item.startDate) : null; if (startDate) { query.createTime = { $gte: startDate.startOf("day").valueOf() } } if (endDate) { query.createTime = query.createTime || {}; query.createTime['$lte'] = endDate.endOf("day").valueOf(); } const nameKeyword = typeof item.name === "string" ? item.name.trim().toLocaleLowerCase() : ""; if (typeof item.medOrgOrderNo === "string" && item.medOrgOrderNo.trim()) { const keyword = item.medOrgOrderNo.trim().replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); query.medOrgOrderNo = { $regex: keyword, $options: "i" }; } if (["YB", "ZF"].includes(item.feeType)) { query.feeType = item.feeType; } try { if (nameKeyword) { // 姓名是加密字段,密文无法直接进行模糊查询。 // 先读取当前筛选范围内的姓名和 ID,解密后匹配,再用 ID 正常分页。 const nameCandidates = await db .collection("diagnostic-record") .find(query, { projection: { name: 1 } }) .toArray(); const matchedIds = decryptDiagnosticRecordList(nameCandidates) .filter( (record) => typeof record.name === "string" && record.name.toLocaleLowerCase().includes(nameKeyword) ) .map((record) => record._id); query._id = { $in: matchedIds }; } const total = await db .collection("diagnostic-record") .countDocuments(query); const list = await db .collection("diagnostic-record") .find(query) .sort({ createTime: -1, _id: -1 }) .skip((page - 1) * pageSize) .limit(pageSize) .toArray(); const decryptedList = decryptDiagnosticRecordList(list); return { success: true, message: "查询成功", list: decryptedList, total, paegs: Math.ceil(total / pageSize), }; } catch (e) { return { success: false, message: e.message || "查询失败" }; } } async function markStoreRxPrinted(item, db, user) { const storeIds = await resolveStoreIds(item, db, user); if ( typeof item.id !== "string" || !ObjectId.isValid(item.id.trim()) || storeIds.length === 0 ) { return { success: false, message: "参数错误" }; } try { const result = await db.collection("diagnostic-record").updateOne( { _id: new ObjectId(item.id.trim()), drugStoreNo: { $in: [...new Set(storeIds)] }, status: status.pass, feeType: "ZF", }, { $inc: { printCount: 1 }, $set: { lastPrintTime: Date.now() }, } ); if (result.matchedCount === 0) { return { success: false, message: "处方不存在或不可打印" }; } return { success: true, message: "打印状态记录成功" }; } catch (e) { return { success: false, message: e.message || "记录打印状态失败" }; } } async function getAuditDiagnosisList(item, db) { const page = item.page > 0 && Number.isInteger(item.page) ? item.page : 1; const pageSize = item.pageSize > 0 && Number.isInteger(item.pageSize) ? item.pageSize : 1; const query = { status: status.init, pharmacistNo: typeof item.pharmacistNo === "string" ? item.pharmacistNo : "", }; // 使用混合搜索支持明文和密文数据 const nameCondition = buildHybridSearchQuery("name", item.name, false); if (nameCondition) { Object.assign(query, nameCondition); } const mobileCondition = buildHybridSearchQuery("mobile", item.mobile, false); if (mobileCondition) { Object.assign(query, mobileCondition); } if (Array.isArray(item.doctorCodes) && item.doctorCodes.length > 0) { query.doctorCode = { $in: item.doctorCodes }; } try { // 并行执行总数查询和最新时间查询 const [total, res] = await Promise.all([ db.collection("diagnostic-record").countDocuments(query), db.collection("diagnostic-record").findOne( {}, { sort: { createTime: -1 }, projection: { createTime: 1, _id: 0 }, } ), ]); // 先查询分页的 diagnostic-record 数据 const diagnosticRecords = await db .collection("diagnostic-record") .find(query) .sort({ prescriptionStartTime: 1 }) .skip((page - 1) * pageSize) .limit(pageSize) .toArray(); if (diagnosticRecords.length === 0) { return { success: true, message: "查询成功", list: [], total, latestTime: res && res.createTime ? res.createTime : 0, }; } // 提取所有 orderId const orderIds = diagnosticRecords.map((record) => record.orderId); // 批量查询对应的 consult-order 数据 const orders = await db .collection("consult-order") .find({ orderId: { $in: orderIds } }) .toArray(); const decryptedOrders = orders.length > 0 ? decryptOrderList(orders) : orders; // 创建 orderId 到 order 的映射 const orderMap = decryptedOrders.reduce((map, order) => { map[order.orderId] = order; return map; }, {}); // 合并数据 const list = diagnosticRecords.map((record) => ({ ...record, order: orderMap[record.orderId] || {}, })); // 解密敏感字段 const decryptedList = decryptDiagnosticRecordList(list); return { success: true, message: "查询成功", list: decryptedList, total, latestTime: res && res.createTime ? res.createTime : 0, }; } catch (e) { return { success: false, message: e.message || "查询失败", latestTime: 0 }; } } async function getPatientDiagnosisRecord(item, db) { if (!item.patientId) { return { success: false, message: "参数错误" }; } const page = item.page > 0 && Number.isInteger(item.page) ? item.page : 1; const orderSource = ["ALIPAY_MINI", "MATEPAD"].includes(item.orderSource) ? item.orderSource : "ALIPAY_MINI"; const pageSize = item.pageSize > 0 && Number.isInteger(item.pageSize) ? item.pageSize : 20; const query = { patientId: item.patientId, status: status.pass, orderSource }; if (typeof item.accountId === 'string' && item.accountId.trim()) { query.accountId = item.accountId.trim() }; const allQuery = query try { const total = await db .collection("diagnostic-record") .countDocuments(allQuery); const list = await db .collection("diagnostic-record") .find(allQuery, { sort: { auditTime: -1 } }) .skip((page - 1) * pageSize) .limit(pageSize) .toArray(); // 查询今日的处方 对已上传但未获取 his处方上传状态的处方进行查询 const start = dayjs().startOf("day").valueOf(); const toSignList = list .filter( (i) => i.patientId && i.medOrgOrderNo && i.status === status.pass && i.uploadStatus === "uploaded" && !i.hisUploadStatus && i.createTime > start ) .map((i) => ({ _id: i._id, patientId: i.patientId, medOrgOrderNo: i.medOrgOrderNo, })); if (toSignList.length > 0) { const task = toSignList.map((data) => zytHis({ type: "getHisPrescriptionUploadStatus", orderno: data.medOrgOrderNo, patientId: data.patientId, _id: data._id, }) ); const res = await Promise.all(task); const result = res.reduce( (acc, item) => { if (item.hisUploadStatus === "uploaded") { acc.uploaded.push(item._id); } else if (item.hisUploadStatus === "error") { acc.uploadError.push(item._id); } else if (item.hisUploadStatus === "notUploaded") { acc.notUploaded.push(item._id); } if (item.hisUploadStatus && item.hisUploadStatus !== "notUploaded") { acc.map[item.orderno] = item.hisUploadStatus; } return acc; }, { uploaded: [], uploadError: [], notUploaded: [], map: {} } ); if (result.uploaded.length > 0) { await db .collection("diagnostic-record") .updateMany( { _id: { $in: result.uploaded } }, { $set: { hisUploadStatus: "uploaded" } } ); } if (result.uploadError.length > 0) { await db .collection("diagnostic-record") .updateMany( { _id: { $in: result.uploadError } }, { $set: { hisUploadStatus: "error" } } ); } if (result.notUploaded.length > 0) { await db .collection("diagnostic-record") .updateMany( { _id: { $in: result.notUploaded }, needReUpload: { $exists: false } }, { $set: { needReUpload: true } } ); } list.forEach((i) => { if (result.map[i.orderId]) { i.hisUploadStatus = result.map[i.orderId]; } }); } // 解密敏感字段 const decryptedList = decryptDiagnosticRecordList(list); return { success: true, message: "查询成功", list: decryptedList, total, paegs: Math.ceil(total / pageSize), }; } catch (e) { return { success: false, message: e.message || "查询失败" }; } } async function getPatientPassDiagnosisRecord(ctx, db) { const { startDate, endDate, patientId } = ctx; if (typeof patientId !== "string" || patientId.trim() === "") { return { success: false, message: "参数错误" }; } const startTime = startDate && dayjs(startDate).isValid() ? dayjs(startDate).startOf("day").valueOf() : ""; const endTime = endDate && dayjs(endDate).isValid() ? dayjs(endDate).endOf("day").valueOf() : ""; try { const query = { patientId, status: status.pass }; if (startTime && endTime) { query.createTime = { $gte: startTime, $lte: endTime }; } const res = await db .collection("diagnostic-record") .find(query) .sort({ createTime: -1 }) .toArray(); // 解密敏感字段 const decryptedRes = decryptDiagnosticRecordList(res); return { success: true, message: "查询成功", data: decryptedRes }; } catch (e) { return { success: false, message: e.message || "查询失败" }; } } async function getAuditedDiagnosisList(item, db) { const page = item.page > 0 && Number.isInteger(item.page) ? item.page : 1; const pageSize = item.pageSize > 0 && Number.isInteger(item.pageSize) ? item.pageSize : 1; const query = { status: { $in: [ status.pass, status.unpass, status.expired, status.invalid, status.passInvalid, status.unpassInvalid, ], }, pharmacistNo: typeof item.pharmacistNo === "string" ? item.pharmacistNo : "", }; // 使用混合搜索支持明文和密文数据 const nameCondition = buildHybridSearchQuery("name", item.name, false); if (nameCondition) { Object.assign(query, nameCondition); } const mobileCondition = buildHybridSearchQuery("mobile", item.mobile, false); if (mobileCondition) { Object.assign(query, mobileCondition); } if (Array.isArray(item.doctorCodes) && item.doctorCodes.length > 0) { query.doctorCode = { $in: item.doctorCodes }; } try { const total = await db .collection("diagnostic-record") .countDocuments(query); // 先查询分页的 diagnostic-record 数据,按审核时间排序 const diagnosticRecords = await db .collection("diagnostic-record") .find(query) .sort({ auditTime: -1 }) .skip((page - 1) * pageSize) .limit(pageSize) .toArray(); if (diagnosticRecords.length === 0) { return { success: true, message: "查询成功", list: [], total, }; } // 提取所有 orderId const orderIds = diagnosticRecords.map((record) => record.orderId); // 批量查询对应的 consult-order 数据 const orders = await db .collection("consult-order") .find({ orderId: { $in: orderIds } }) .toArray(); const decryptedData = orders ? decryptOrderList(orders) : orders; // 创建 orderId 到 order 的映射 const orderMap = decryptedData.reduce((map, order) => { map[order.orderId] = order; return map; }, {}); // 合并数据 const list = diagnosticRecords.map((record) => ({ ...record, order: orderMap[record.orderId] || {}, })); // 解密敏感字段 const decryptedList = decryptDiagnosticRecordList(list); return { success: true, message: "查询成功", list: decryptedList, total, }; } catch (e) { return { success: false, message: e.message || "查询失败" }; } } async function auditDiagnosis(item, db) { // const { pharmacistNo, pharmacist, corpId } = item; const pharmacistNo = item && typeof item.pharmacistNo === 'string' ? item.pharmacistNo : ''; const corpId = item && typeof item.corpId === 'string' ? item.corpId : ''; if (!corpId || !pharmacistNo) { return { success: false, message: "参数错误" }; } if (![status.pass, status.unpass].includes(item.status)) { return { success: false, message: "状态错误" }; } const ids = Array.isArray(item.ids) ? item.ids.filter(i => typeof i === 'string' && i.trim() !== '') : []; if (ids.length === 0) { return { success: false, message: "id错误" }; } const yaoshi = await db.collection('hlw-doctor').findOne({ corpId, doctorNo: pharmacistNo }); if (!yaoshi) { return { success: false, message: "审方药师不存在" }; } const pharmacist = yaoshi.doctorName; const pharmacistCAUserId = yaoshi.signatureUrl; const auditSource = item.operationSource === "AUTO" ? "AUTO" : "MANUAL"; const reasons = Array.isArray(item.reasons) ? item.reasons.filter((i) => typeof i === "string" && i.trim() !== "") : []; if (item.status === status.unpass && reasons.length === 0) { return { success: false, message: "请填写拒绝原因" }; } try { // 1. 根据待审核id 查询待处理的处方记录 const _rxList = await db.collection("diagnostic-record").find( { _id: { $in: ids.map((id) => new ObjectId(id)) }, status: status.init, pharmacistNo }, ).toArray(); // 患者姓名 证件号 手机号解密 const rxList = _rxList.map(i => decryptDiagnosticRecordFields(i)); // 2. 根据处方记录的orderId 查询对应的咨询记录 const consultOrderList = await db.collection("consult-order").find( { orderId: { $in: rxList.map((i) => i.orderId) } }, { projection: { _id: 1, orderId: 1, orderStatus: 1, medorg_order_no: 1 } } ).toArray(); //3. 根据咨询记录的状态 将待处理的处方列表分为三组 passList, unPassList , expireList, failList const failList = []; const passList = []; const unPassList = []; const expireList = []; const targetList = item.status === status.pass ? passList : unPassList; for (let id of ids) { const rx = rxList.find(i => i._id.toString() === id); if (!rx) { failList.push({ id, message: "处方不存在" }); continue; } const data = { rx, status: item.status }; const order = consultOrderList.find(i => i.orderId === rx.orderId && rx.orderId); data.order = order; if (order && ["pending", "processing", "completed"].includes(order.orderStatus)) { targetList.push(data); } else if (order) { data.status = status.expired; expireList.push(data) } else { failList.push({ id, message: "咨询订单不存在" }); } } const statsCount = { allCount: targetList.length + expireList.length, // 总待处理条数 handleCount: 0, // 审核通过或者审核不通过的处理条数 expireCount: 0 // 已过期处理成功条数 } // 4. 根据分组 分别处理 if (passList.length) { let successRxList = []; const uploadResult = await Promise.all(passList.map(i => uploadRx2His(i.rx, i.order))); uploadResult.forEach(i => { if (i.success) { const rx = passList.find(j => j.rx._id.toString() === i.id.toString()); successRxList.push(rx); } else { failList.push({ id: i.id, message: i.message }); } }) const { modifiedCount } = await db.collection("diagnostic-record").updateMany( { _id: { $in: successRxList.map(i => i.rx._id) }, status: status.init, }, { $set: { uploadStatus: "uploaded", status: status.pass, auditTime: Date.now(), pharmacistSignImg: pharmacistCAUserId, auditSource } } ) statsCount.handleCount += modifiedCount; // 处方签名 // successRxList.forEach(({ rx }) => sign(rx, pharmacistCAUserId)) // 发送审核通过消息 successRxList.forEach(({ rx }) => attempToSendPassMessage(rx, item.corpId)) } if (unPassList.length) { const { modifiedCount } = await db.collection("diagnostic-record").updateMany( { _id: { $in: unPassList.map(i => i.rx._id) }, status: status.init }, { $set: { status: item.status, auditTime: Date.now(), reasons, auditSource } } ) statsCount.handleCount += modifiedCount; await db.collection("consult-order").updateMany( { orderId: { $in: unPassList.map(i => i.rx.orderId) } }, { $set: { orderStatus: "processing" } } ) unPassList.forEach(({ rx }) => attempToSendUnPassMessage(rx, item.corpId, pharmacist, reasons)) // 记录一下 药师拒绝的操作 db.collection("pharmacist-reject-record").insertMany( unPassList.map((item) => ({ recordId: item.rx._id.toString(), type: "pharmacistReject", pharmacistNo, pharmacist, reasons, createTime: Date.now(), })) ) } if (expireList.length) { const { modifiedCount } = await db.collection("diagnostic-record").updateMany( { _id: { $in: expireList.map(i => i.rx._id) }, status: status.init }, { $set: { status: status.expired, auditTime: Date.now(), auditSource, updateTime: Date.now(), }, } ) statsCount.expireCount += modifiedCount; } return { success: statsCount.handleCount > 0, message: `已审核${item.status === status.pass ? "通过" : "不通过"}${statsCount.handleCount}条数据${statsCount.expireCount > 0 ? `, 已过期${statsCount.expireCount}条数据` : ''}`, failList } } catch (e) { return { success: false, message: e.message || "审核失败" }; } } async function uploadRx2His(rx, order) { try { const uploadData = formatUploadRxData(rx, order); console.log('='.repeat(10)) console.log('='.repeat(10)) console.log(JSON.stringify(uploadData)) const { success, message } = await hnHis({ type: 'hlwuploadprescription', data: uploadData }) return { success, message, id: rx._id } } catch (e) { return { success: false, message: `[审核处方上传错误]: ${e.message}`, id: rx._id } } } async function attempToSendPassMessage(rx, corpId) { for (let i = 0; i < 10; i++) { try { const { success, message } = await tencentIM({ type: "sendSystemNotification", corpId, formAccount: rx.doctorCode, toAccount: rx.orderId, SyncOtherMachine: 1, msgBody: [ { MsgType: "TIMCustomElem", MsgContent: { Data: "AUDITPASS", Ext: JSON.stringify({ id: rx._id, orderId: rx.orderId, name: rx.name, expireTime: rx.expireTime ? dayjs(rx.expireTime).format("YYYY-MM-DD HH:mm") : '', time: dayjs(rx.createTime).format("YYYY-MM-DD HH:mm"), sex: rx.sex, age: rx.age, prescriptionType: rx.prescriptionType, pickUpType: rx.pickUpType, disease: rx.diagnosisList.map(d => d.name).join(", "), drugs: rx.drugs.map(d => ({ drugName: d.drugName, spec: d.specification, quantity: d.quantity, unit: d.unit, usageName: d.usageName, frequencyName: d.frequencyName, days: d.days, dosage: d.dosage, dosage_unit: d.dosage_unit })), wines: Array.isArray(rx.wines) ? rx.wines.map(w => ({ name: w.name, quantity: w.quantity, details: w.details, efficacy: w.efficacy, })) : [] }) }, }, ], }) if (success) { break; } console.log(`[发送处方审核通过消息失败(第${i + 1}次)]: ${message}`); await new Promise(resolve => setTimeout(resolve, 500 + 100 * i)); } catch (e) { console.log(`[发送处方审核通过消息错误(第${i + 1}次)]: ${e.message}`); } } } async function attempToSendUnPassMessage(rx, corpId, pharmacist, reasons) { for (let i = 0; i < 10; i++) { try { const { success, message } = await tencentIM({ type: "sendSystemNotification", corpId, formAccount: rx.orderId, toAccount: rx.doctorCode, SyncOtherMachine: 2, msgBody: [ { MsgType: "TIMCustomElem", MsgContent: { Data: "AUDIFAIL", Desc: "notification", Ext: `药师${pharmacist}审方不通过,驳回原因:${reasons.join(",")}`, }, }, ], }) if (success) { break; } console.log(`[发送处方审核通过消息失败(第${i + 1}次)]: ${message}`); await new Promise(resolve => setTimeout(resolve, 500 + 100 * i)); } catch (e) { console.log(`[发送处方审核通过消息错误(第${i + 1}次)]: ${e.message}`); } } } // 对医生和药师进行签名 async function sign(item, pharmacistCAUserId) { const { doctorCAUserId, pharmacist } = item; console.log("进入签名"); const oriData = getOriData(item); const doctorRes = await conditionalCallBeijingCa( { type: "autoSign", userId: doctorCAUserId, oriData, }, db ); const pharmacistRes = await conditionalCallBeijingCa( { type: "autoSign", userId: pharmacistCAUserId, oriData: `${oriData} 药师${pharmacist}审方通过`, }, db ); console.log("医生签名结果", doctorRes); console.log("药师签名结果", pharmacistRes); } function getOriData(data) { const doctorName = data.doctorName; const patientName = data.name; const diagnosis = data.diagnosisList.map((d) => d.name).join(", "); const drugs = data.drugs .map( (d) => `${d.drugName} (${d.dosage}${d.dosage_unit}, ${d.frequencyName}, ${d.days}天)` ) .join(", "); return `医生${doctorName}向患者${patientName}开具的诊断是${diagnosis},开具的药物是${drugs}。`; } async function uploadDiagnosis(ids) { if (!Array.isArray(ids) || ids.length == 0) return { success: false, message: "参数错误", }; try { const res = await db .collection("diagnostic-record") .find({ _id: { $in: ids }, status: status.pass }) .limit(ids.length) .toArray(); const list = res.map((item) => ({ payload: formatUploadRxData(item), _id: item._id, })); const res1 = await Promise.all( list.map((item) => zytHis({ type: "hlwuploadprescription", payload: item.payload, _id: item._id, }) ) ); const successIds = res1.filter((i) => i.success).map((i) => i._id); await db .collection("diagnostic-record") .updateMany( { _id: { $in: successIds } }, { $set: { uploadStatus: "uploaded" } } ); const successCount = successIds.length; return { success: true, message: `成功上传${successCount}条数据`, }; } catch (err) { console.log(err, err.message); return { success: false, message: err.message || "上传失败", }; } } /** * * orderStatus completed(已完成) processing(处理中) pending(待处理) cancelled(已取消) finished(已结束) * syncOtherMachine 若不希望将消息同步至 From_Account,则 SyncOtherMachine 填写2;若希望将消息同步至 From_Account,则 SyncOtherMachine 填写1。 * MsgType TIMTextElem(文本消息) TIMLocationElem(位置消息) TIMFaceElem(表情消息) TIMCustomElem(自定义消息) TIMSoundElem(语音消息) TIMImageElem(图像消息) TIMFileElem(文件消息) TIMVideoFileElem(视频消息) * MsgContent.Text 文本消息内容 * MsgContent.Data 自定义消息类型 MEDICALADVICE(医嘱) * MsgContent.Desc notification * */ async function updateOrderStatusAndSendNotification(item, timeData) { const { orderId, doctorCode, corpId, notification, syncOtherMachine, orderStatus, msgType, } = item; await consultOrder( { type: "updateConsultOrderStatus", orderId, orderStatus, corpId, }, db ); if (timeData) { timeData.updateOrderStatusTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); } await tencentIM({ type: "sendSystemNotification", corpId, formAccount: orderId, toAccount: doctorCode, SyncOtherMachine: syncOtherMachine, msgBody: [ { MsgType: "TIMCustomElem", MsgContent: { Data: msgType, Desc: "notification", Ext: notification, }, }, ], }); if (timeData) { timeData.sendNotificationTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); } } async function getLatestSubmitTime(ctx) { try { const pharmacistNo = typeof ctx.pharmacistNo === "string" ? ctx.pharmacistNo : ""; if (!pharmacistNo) { return { success: true, message: "获取成功", data: 0, }; } const res = await db.collection("diagnostic-record").findOne( { pharmacistNo }, { sort: { createTime: -1 }, // Sort by createTime in descending order projection: { createTime: 1, _id: 0 }, // Only return the createTime field, exclude _id } ); const reUploadCount = await db.collection('diagnostic-record').countDocuments({ prescriptionType: 'storeMedicinePurchase', pharmacistNo, needReUpload: true, createTime: { $gt: dayjs().startOf('day').valueOf() } }); return { success: true, message: "获取成功", data: res ? res.createTime : 0, reUploadCount }; } catch (e) { return { success: false, message: e.message || "获取失败", data: 0 }; } } async function orderHasDiagnosis(item) { if (typeof item.patientId !== "string" || typeof item.orderId !== "string") { return { success: false, message: "参数错误", }; } try { const count = await db.collection("diagnostic-record").countDocuments({ patientId: item.patientId, orderId: item.orderId, }); return { success: true, message: "查询成功", exist: count > 0 }; } catch (e) { return { success: true, message: "查询成功", exist: false }; } } async function reUploadDiagnosticRecord({ _id }) { try { const data = await db .collection("diagnostic-record") .findOne({ _id: new ObjectId(_id) }); if (!data) return { success: false, message: "处方不存在" }; if (data.prescriptionType !== "storeMedicinePurchase") { return { success: false, message: "暂只支持门店处方重新上传" }; } if (data && data.uploadStatus === "uploaded") return { success: false, message: "处方已上传" }; const { uploaded } = await zytHis({ type: "hlwPrescriptionOrderStatus", orderno: data.orderId, patientId: data.patientId, }); if (uploaded) { await db .collection("diagnostic-record") .updateOne( { _id: new ObjectId(_id) }, { $set: { uploadStatus: "uploaded", hisUploadStatus: "uploaded" } } ); return { success: true, message: "处方上传成功" }; } const payload = formatUploadRxData(data); const { success, message } = await zytHis({ type: "hlwuploadprescription", payload, _id: data._id, }); if (success) { await db .collection("diagnostic-record") .updateOne( { _id: new ObjectId(_id) }, { $set: { uploadStatus: "uploaded" } } ); return { success: true, message: "处方上传成功" }; } return { success: false, message: message || "重新上传失败" }; } catch (e) { return { success: false, message: e.message || "重新上传失败" }; } } // 废弃处方 async function discardDiagnosticRecord(ctx) { const { orderId } = ctx; if (typeof orderId !== "string" || orderId.trim() === "") return { success: false, message: "参数错误" }; try { const item = await db.collection("diagnostic-record").findOne( { orderId, status: { $nin: [status.passInvalid, status.invalid, status.unpassInvalid], }, }, { projection: { status: 1, _id: 1 } } ); if (item) { const targetStatus = item.status === status.pass ? status.passInvalid : item.status === status.unpass ? status.unpassInvalid : status.invalid; await db.collection("diagnostic-record").updateOne( { _id: item._id }, { $set: { status: targetStatus, }, } ); return { success: true, message: "废弃成功" }; } return { success: false, message: "处方不存在" }; } catch (e) { return { success: false, message: e.message || "废弃失败" }; } } async function getAllDiagnosisRecord(ctx) { const { page, pageSize } = ctx; const pageNum = page > 0 && Number.isInteger(page) ? page : 1; const size = pageSize > 0 && Number.isInteger(pageSize) ? pageSize : 20; const insuranceCodes = Array.isArray(ctx.insuranceCodes) ? ctx.insuranceCodes.filter(i => typeof i === 'string' && i.trim()) : []; try { const query = {}; // 使用混合搜索支持明文和密文数据 const nameCondition = buildHybridSearchQuery("name", ctx.name, false); if (nameCondition) { Object.assign(query, nameCondition); } if (Array.isArray(ctx.doctorCodes) && ctx.doctorCodes.length > 0) { query.doctorCode = { $in: ctx.doctorCodes }; } if (Array.isArray(ctx.pharmacistNos) && ctx.pharmacistNos.length > 0) { query.pharmacistNo = { $in: ctx.pharmacistNos }; } const { startDate, endDate } = ctx; if (startDate && dayjs(startDate).isValid()) { query.createTime = { $gte: dayjs(startDate).startOf("day").valueOf() }; } if (endDate && dayjs(endDate).isValid()) { query.createTime = { ...(query.createTime || {}), $lte: dayjs(endDate).endOf("day").valueOf(), }; } const { auditStartDate, auditEndDate } = ctx; if (auditStartDate && dayjs(auditStartDate).isValid()) { query.auditTime = { $gte: dayjs(auditStartDate).startOf("day").valueOf(), }; } if (auditEndDate && dayjs(auditEndDate).isValid()) { query.auditTime = { ...(query.auditTime || {}), $lte: dayjs(auditEndDate).endOf("day").valueOf(), }; } if (Array.isArray(ctx.status)) { query.status = { $in: ctx.status }; } if (ctx.preType === 'chinese') { query.prescriptionType = 'medicalWinePurchase'; } else if (ctx.preType === 'west') { query.prescriptionType = { $ne: 'medicalWinePurchase' }; } if (insuranceCodes.length) { query.drugs = { $elemMatch: { insurance_code: { $in: insuranceCodes } } } } const total = await db .collection("diagnostic-record") .countDocuments(query); const list = await db.collection("diagnostic-record").aggregate([ { $match: query }, { $sort: { createTime: -1 } }, { $skip: (pageNum - 1) * size }, { $limit: size }, { $lookup: { from: "consult-order", localField: "orderId", foreignField: "orderId", as: "orderInfo" } }, { $addFields: { sex: { $arrayElemAt: ["$orderInfo.sex", 0] }, age: { $arrayElemAt: ["$orderInfo.age", 0] } } }, { $project: { orderInfo: 0 } } ]).toArray() // 解密敏感字段 const decryptedList = decryptDiagnosticRecordList(list); const pages = Math.ceil(total / size); // 兼容旧用法(直接返回 list/total),同时为 PC 端提供 data 包装 return { success: true, message: "查询成功", list: decryptedList, total, pages, page: pageNum, pageSize: size, data: { list: decryptedList, total, pages, page: pageNum, pageSize: size, }, }; } catch (e) { return { success: false, message: e.message || "查询失败" }; } } async function getRecommendPharmacist({ pharmacistNo = "", type = 'west' }) { try { const query = { job: "pharmacist", onlineStatus: "online", serviceStatus: "enabled", } if (type === 'chinese') { query.openChinesePres = true; } else { query.openWestPres = true } const nos = await db .collection("hlw-doctor") .find( query, { projection: { doctorNo: 1, doctorName: 1 } } ) .toArray(); const pharmacistNos = nos.map((item) => item.doctorNo); const matchPharmacist = nos.find( (item) => pharmacistNo && item.doctorNo === pharmacistNo ); if (matchPharmacist) { return { success: true, message: "获取推荐药师成功", data: matchPharmacist, }; } if (nos.length === 0) { return { success: false, message: "当前没有药师在线,请稍后处理" }; } const res = await getTodayPharmacistCount({ pharmacistNos }, db); if (!res.success) { return res; } const { data: [countMap], } = res; const auditCount = countMap && Array.isArray(countMap.auditCount) ? countMap.auditCount : []; const auditedCount = countMap && Array.isArray(countMap.auditedCount) ? countMap.auditedCount : []; const list = nos .map((item) => { const audit = auditCount.find((i) => i._id === item.doctorNo); const audited = auditedCount.find((i) => i._id === item.doctorNo); const auditNum = audit ? audit.count : 0; const auditedNum = audited ? audited.count : 0; return { pharmacistNo: item.doctorNo, doctorNo: item.doctorNo, pharmacistName: item.doctorName, doctorName: item.doctorName, weight: new BigNumber(auditNum) .plus(new BigNumber(auditedNum).dividedBy(1000000)) //权重计算 已经审核过的权重占比小一点 .toNumber(), }; }) .sort((a, b) => a.weight - b.weight); // 按照权重升序排序 const [{ weight }] = list; const lightList = list.filter((i) => i.weight === weight); // 取出权重最小的药师 const randomIndex = Math.floor(Math.random() * lightList.length); // 随机选取一个药师 const pharmacist = lightList[randomIndex]; return { success: true, message: "获取推荐药师成功", data: pharmacist }; } catch (e) { return { success: false, message: e.message || "获取推荐药师失败", }; } } async function getTodayPharmacistCount(ctx) { const pharmacistNos = Array.isArray(ctx.pharmacistNos) ? ctx.pharmacistNos.filter((i) => typeof i === "string") : [ctx.pharmacistNos]; try { const res = await db .collection("diagnostic-record") .aggregate([ { $match: { pharmacistNo: { $in: pharmacistNos }, createTime: { $gt: dayjs().startOf("day").valueOf(), $lte: dayjs().endOf("day").valueOf(), }, }, }, { $facet: { auditCount: [ { $match: { status: status.init } }, { $group: { _id: "$pharmacistNo", count: { $sum: 1 } } }, ], auditedCount: [ { $match: { status: { $in: [status.pass, status.unpass] } } }, { $group: { _id: "$pharmacistNo", count: { $sum: 1 } } }, ], }, }, ]) .toArray(); return { success: true, message: "查询成功", data: res }; } catch (e) { return { success: false, message: e.message || "查询失败" }; } } async function getDoctorRxStats(ctx) { const { doctorCodes, startDate, endDate } = ctx; const query = {}; if (Array.isArray(doctorCodes)) { query.doctorCode = { $in: doctorCodes }; } if (startDate && dayjs(startDate).isValid()) { query.createTime = { $gte: dayjs(startDate).startOf("day").valueOf() }; } if (endDate && dayjs(endDate).isValid()) { query.createTime = { ...(query.createTime || {}), $lte: dayjs(endDate).endOf("day").valueOf(), }; } if (typeof ctx.orderSource === "string" && ctx.orderSource.trim() !== "") { query.orderSource = ctx.orderSource; } console.log("查询医生处方统计", query); try { const res = await db .collection("diagnostic-record") .aggregate( [ { $match: query }, { $group: { _id: "$doctorCode", doctorName: { $first: "$doctorName" }, count: { $sum: 1 }, passCount: { $sum: { $cond: [{ $eq: ["$status", status.pass] }, 1, 0] }, }, passInvalidCount: { $sum: { $cond: [{ $eq: ["$status", status.passInvalid] }, 1, 0], }, }, }, }, ], { allowDiskUse: true } ) .toArray(); return { success: true, message: "查询成功", data: res }; } catch (e) { return { success: false, message: e.message || "查询失败" }; } } async function getPharmacistRxStats(ctx) { const { pharmacistNos, startDate, endDate } = ctx; const query = {}; if (Array.isArray(pharmacistNos)) { query.pharmacistNo = { $in: pharmacistNos }; } if (startDate && dayjs(startDate).isValid()) { query.createTime = { $gte: dayjs(startDate).startOf("day").valueOf() }; } if (endDate && dayjs(endDate).isValid()) { query.createTime = { ...(query.createTime || {}), $lte: dayjs(endDate).endOf("day").valueOf(), }; } try { const res = await db .collection("diagnostic-record") .aggregate([ { $match: query }, { $group: { _id: "$pharmacistNo", doctorName: { $first: "$pharmacist" }, count: { $sum: 1 }, auditCount: { $sum: { $cond: [ { $in: [ "$status", [status.pass, status.invalid, status.passInvalid], ], }, 1, 0, ], }, }, expiredCount: { $sum: { $cond: [{ $eq: ["$status", status.expired] }, 1, 0] }, }, }, }, ]) .toArray(); const rejectRes = await db .collection("pharmacist-reject-record") .aggregate([ { $match: query }, { $group: { _id: "$pharmacistNo", doctorName: { $first: "$pharmacist" }, rejectCount: { $sum: 1 }, }, }, ]) .toArray(); return { success: true, message: "查询成功", data: res, rejectRes }; } catch (e) { return { success: false, message: e.message || "查询失败" }; } } async function checkRxUploadStatus(ctx) { const { orderId, registerId, patientId } = ctx; if (typeof orderId !== "string" || orderId.trim() === "") { return { success: false, message: "订单号不能为空" }; } if (typeof registerId !== "string" || registerId.trim() === "") { return { success: false, message: "挂号号不能为空" }; } if (typeof patientId !== "string" || patientId.trim() === "") { return { success: false, message: "患者id不能为空" }; } try { const rx = await db.collection("diagnostic-record").findOne({ orderId, patientId }); if (!rx) { return { success: false, message: "处方不存在" }; } const { status } = await zytHis({ type: "hlwPrescriptionOrderStatus", orderno: rx.medOrgOrderNo, patientId, }); if (status === "0") { return { success: true, message: "正在获取订单信息,请等待1分钟后再发起退费申请!", }; } else if (["1", "2", "3"].includes(status)) { return { success: true, message: "处方已上传或者已经作废" }; } return { success: false, message: "处方上传状态未知,请稍后再试" }; } catch (e) { return { success: false, message: e.message || "查询失败" }; } } async function getAccountRxList(ctx) { try { if (typeof ctx.accountId !== 'string' || ctx.accountId.trim() === '') { return { success: false, message: "账号id不能为空" }; } const page = Number.isInteger(ctx.page) && ctx.page > 0 ? ctx.page : 1; const pageSize = Number.isInteger(ctx.pageSize) && ctx.pageSize > 0 ? ctx.pageSize : 10; const query = { corpId: ctx.corpId, orderSource: "ALIPAY_MINI", accountId: ctx.accountId.trim(), status: status.pass }; const [list, total] = await Promise.all([ db.collection("diagnostic-record").find(query, { projection: types.output.accountRxList }).sort({ createTime: -1 }).skip((page - 1) * pageSize).limit(pageSize).toArray(), db.collection("diagnostic-record").countDocuments(query), ]); await getHisPrescriptionUploadStatus(list); // 获取处方单号 const rpNos = list.filter(i => i.prescriptionType === 'onlineMedicinePurchase').map(i => i._id.toString()); const orderList = await db.collection("medicine-purchase-order").find({ corpId: ctx.corpId, rpNo: { $in: rpNos } }, { projection: types.output.medicinePurchaseOrder }).toArray(); return { success: true, message: "查询成功", orderList, list: decryptDiagnosticRecordList(list), total, pages: Math.ceil(total / pageSize), now: Date.now() }; } catch (e) { return { success: false, message: `获取处方记录失败:${e?.message || '未知错误 '}` }; } } async function getRxDetail(ctx) { try { if (typeof ctx.id !== 'string' || ctx.id.trim() === '') { return { success: false, message: "处方id不能为空" }; } const query = {}; query._id = new ObjectId(ctx.id.trim()); if (typeof ctx.accountId === 'string' && ctx.accountId.trim() !== '') { query.accountId = ctx.accountId.trim(); } const record = await db.collection("diagnostic-record").findOne(query); if (!record) { return { success: false, message: "处方不存在" }; } if (record.prescriptionType === 'onlineMedicinePurchase') { const order = await db.collection("medicine-purchase-order").findOne({ rpNo: record._id.toString() }, { projection: types.output.medicinePurchaseOrder }); if (order) { record.purchaseOrder = order; } } return { success: true, message: "查询成功", data: decryptDiagnosticRecordFields(record), now: Date.now() }; } catch (e) { return { success: false, message: `获取处方详情失败:${e?.message || '未知错误 '}` }; } } async function getHisPrescriptionUploadStatus(list) { try { const toSignList = list.filter(i => i.prescriptionType === 'storeMedicinePurchase' && i.medOrgOrderNo && i.hisUploadStatus === 'uploaded' && !i.hisUploadStatus && i.createTime > dayjs().startOf("day").valueOf()).map(i => ({ _id: i._id, patientId: i.patientId, medOrgOrderNo: i.medOrgOrderNo, })); if (toSignList.length > 0) { const task = toSignList.map((data) => zytHis({ type: "getHisPrescriptionUploadStatus", orderno: data.medOrgOrderNo, patientId: data.patientId, _id: data._id, }) ); const res = await Promise.all(task); const result = res.reduce( (acc, item) => { if (item.hisUploadStatus === "uploaded") { acc.uploaded.push(item._id); } else if (item.hisUploadStatus === "error") { acc.uploadError.push(item._id); } else if (item.hisUploadStatus === "notUploaded") { acc.notUploaded.push(item._id); } if (item.hisUploadStatus && item.hisUploadStatus !== "notUploaded") { acc.map[item.orderno] = item.hisUploadStatus; } return acc; }, { uploaded: [], uploadError: [], notUploaded: [], map: {} } ); if (result.uploaded.length > 0) { db.collection("diagnostic-record") .updateMany( { _id: { $in: result.uploaded } }, { $set: { hisUploadStatus: "uploaded" } } ); } if (result.uploadError.length > 0) { db.collection("diagnostic-record") .updateMany( { _id: { $in: result.uploadError } }, { $set: { hisUploadStatus: "error" } } ); } if (result.notUploaded.length > 0) { await db .collection("diagnostic-record") .updateMany( { _id: { $in: result.notUploaded }, needReUpload: { $exists: false } }, { $set: { needReUpload: true } } ); } list.forEach((i) => { if (result.map[i.orderId]) { i.hisUploadStatus = result.map[i.orderId]; } }); } } catch (e) { console.log('查询his上传信息错误: ', e.message) } } async function getAccountHistoryDrugs(ctx) { try { const patientId = typeof ctx.patientId === 'string' && ctx.patientId.trim() !== '' ? ctx.patientId.trim() : ''; // const accountId = typeof ctx.accountId === 'string' && ctx.accountId.trim() !== '' ? ctx.accountId.trim() : ''; if (patientId == '') { return { success: false, message: "患者id不能为空" }; } const query = { patientId, status: status.pass }; const page = Number.isInteger(ctx.page) && ctx.page > 0 ? ctx.page : 1; const pageSize = Number.isInteger(ctx.pageSize) && ctx.pageSize > 0 ? ctx.pageSize : 10; const [list, total] = await Promise.all([ db.collection("diagnostic-record").find(query, { projection: types.output.accountHistoryDrugs }).sort({ createTime: -1 }).skip((page - 1) * pageSize).limit(pageSize).toArray(), db.collection("diagnostic-record").countDocuments(query), ]); return { success: true, message: "查询成功", data: list, total, pages: Math.ceil(total / pageSize) }; } catch (e) { return { success: false, message: `获取账号历史药品错误:${e?.message || '未知错误 '}` }; } } async function getPatientHistoryDrugs(ctx) { try { const patientId = typeof ctx.patientId === 'string' && ctx.patientId.trim() !== '' ? ctx.patientId.trim() : ''; if (patientId.value == '') { return { success: false, message: "患者id不能为空" }; } const query = { patientId, status: status.pass, prescriptionType: { $ne: 'medicalWinePurchase' } }; const page = Number.isInteger(ctx.page) && ctx.page > 0 ? ctx.page : 1; const pageSize = Number.isInteger(ctx.pageSize) && ctx.pageSize > 0 ? ctx.pageSize : 10; const [list, total] = await Promise.all([ db.collection("diagnostic-record").find(query, { projection: types.output.accountHistoryDrugs }).sort({ createTime: -1 }).skip((page - 1) * pageSize).limit(pageSize).toArray(), db.collection("diagnostic-record").countDocuments(query), ]); return { success: true, message: "查询成功", data: list, total, pages: Math.ceil(total / pageSize) }; } catch (e) { return { success: false, message: `获取患者历史药品错误:${e?.message || '未知错误 '}` }; } } /** * 获取药品的his收费信息(药品列表中添加his收费信息) * @param {*} medicines 药品列表 * @param {*} areaCode 医保区域代码 * @returns */ async function getMedicinesWithHisChargeInfo(medicines, areaCode) { try { if (!Array.isArray(medicines) || medicines.length === 0) { return { success: false, message: "药品列表不能为空" }; } if (typeof areaCode !== 'string' || areaCode.trim() === '') { return { success: false, message: "区域代码不能为空" }; } const insuranceCodes = medicines.map(i => i.insurance_code); const { success, message, data } = await zytHis({ type: 'getHisDrugInfo', insuranceCodes, areaCode }); if (!success) { return { success, message }; } const hisDrugs = data && Array.isArray(data) ? data : []; const drugs = []; for (const med of medicines) { const hisDrug = hisDrugs.find(j => med.insurance_code && med.insurance_code === j.gjbm); if (!hisDrug) { return { success: false, messsage: `药品【${med.drugName}】医保编码在his中不存在` }; } const quantity = Number.isInteger(med.quantity) && med.quantity > 0 ? med.quantity : 0; if (quantity === 0) { return { success: false, messsage: `药品【${med.drugName}】数量不是正整数` }; } const pack_retprice = Number(hisDrug.pack_retprice) == hisDrug.pack_retprice && hisDrug.pack_retprice > 0 && hisDrug.pack_retprice !== '' ? Number(hisDrug.pack_retprice) : 0; if (pack_retprice === 0 || !Number.isInteger(pack_retprice * 100)) { return { success: false, messsage: `药品【${med.drugName}】价格无效(${pack_retprice})` }; } const totalPrice = new BigNumber(pack_retprice).multipliedBy(quantity).toNumber(); drugs.push({ ...med, hisChargeInfo: { drugGroupNo: hisDrug.group_no, drugId: hisDrug.charge_code, drugSerial: hisDrug.serial, drugPrice: hisDrug.pack_retprice, drugSumamt: totalPrice } }) } return { success: true, drugs }; } catch (e) { return { success: false, message: `获取his药品信息错误: ${e.message}` } } } async function getWinesWithHisChargeInfo(data) { if (!Array.isArray(data) || data.length === 0) { return { success: false, message: "浸酒信息错误" }; } const wineIds = data.map(i => new ObjectId(i._id)); const docs = await db.collection('chinese-medicine').find({ _id: { $in: wineIds } }).toArray(); const result = []; for (let i of data) { const wine = docs.find(item => i._id === item._id.toString()); if (!wine) { return { success: false, message: `未查询到浸酒【${i.name}】信息` }; } const quantity = Number.isInteger(i.quantity) && i.quantity > 0 ? i.quantity : 0; if (quantity === 0) { return { success: false, message: `浸酒【${i.name}】数量错误` }; } result.push({ _id: wine._id.toString(), name: wine.name, details: wine.details, efficacy: wine.efficacy, hisId: wine.hisId, unit: wine.unit, quantity: i.quantity, }) } return { success: true, message: '获取浸酒价格信息成功', wines: result } } async function getRxMedicineOrderStatus(ctx, db) { try { const id = typeof ctx.id === 'string' && ctx.id.trim() !== '' ? ctx.id.trim() : ''; const accountId = typeof ctx.accountId === 'string' && ctx.accountId.trim() !== '' ? ctx.accountId.trim() : ''; if (!id || !accountId) { return { success: false, message: "处方id或账号id不能为空" }; } const rx = await db.collection('diagnostic-record').findOne({ _id: new ObjectId(id), accountId, prescriptionType: 'onlineMedicinePurchase' }, { projection: { expireTime: 1 } }); if (!rx) { return { success: false, message: "处方不存在" }; } const expired = Date.now() > rx.expireTime; const order = await db.collection('medicine-purchase-order').findOne({ rpNo: id, accountId }, { projection: { status: 1 } }); return { success: true, message: "查询成功", data: { id: rx._id, expired, orderStatus: order ? order.status : 'none' } }; } catch (e) { return { success: false, message: `获取账号购药处方记录失败: ${e.message}` } } } async function getYbReviewResult(ctx, db) { const params = ctx && ctx.params && typeof ctx.params === "object" ? ctx.params : {}; const orderId = typeof params.orderId === "string" ? params.orderId.trim() : ""; const drugs = Array.isArray(params.drugs) ? params.drugs : []; const diagnosisList = Array.isArray(params.diagnosisList) ? params.diagnosisList : []; const corpId = typeof ctx.corpId === "string" ? ctx.corpId.trim() : ""; if (!orderId) { return { success: false, message: "订单号不能为空" }; } if (drugs.length === 0) { return { success: false, message: "药品信息不能为空" }; } if (diagnosisList.length === 0) { return { success: false, message: "诊断信息不能为空" }; } try { // 订单中的患者、医保及就诊信息是审核请求的基础数据;药品以本次开方页面提交的数据为准。 const orderQuery = { orderId }; if (corpId) { orderQuery.corpId = corpId; } const order = await db.collection("consult-order").findOne(orderQuery); if (!order) { return { success: false, message: "订单不存在" }; } const drugIds = drugs.map((drug) => drug && typeof drug._id === "string" ? drug._id.trim() : "" ); const invalidDrugId = drugIds.find((id) => !ObjectId.isValid(id)); if (invalidDrugId !== undefined) { return { success: false, message: "药品ID无效" }; } const normalizedDrugIds = drugIds.map((id) => new ObjectId(id).toString()); const dbDrugs = await db .collection("drug-info") .find( { _id: { $in: [...new Set(normalizedDrugIds)].map((id) => new ObjectId(id)), }, }, { projection: { erpId: 1, insurance_code: 1, name: 1, dosage_form: 1, specification: 1, }, } ) .toArray(); const dbDrugMap = new Map( dbDrugs.map((drug) => [drug._id.toString(), drug]) ); const missingDrugId = normalizedDrugIds.find((id) => !dbDrugMap.has(id)); if (missingDrugId) { return { success: false, message: `未匹配到药品信息:${missingDrugId}` }; } const reviewDrugs = drugs.map((drug, index) => { const dbDrug = dbDrugMap.get(normalizedDrugIds[index]); return { erpId: dbDrug.erpId, insurance_code: dbDrug.insurance_code, _id: dbDrug._id.toString(), drugName: dbDrug.name, dosage_form: dbDrug.dosage_form, specification: dbDrug.specification, quantity: drug.quantity, usageCode: drug.usageCode, usageName: drug.usageName, days: drug.days, frequencyCode: drug.frequencyCode, frequencyName: drug.frequencyName, dosage: drug.dosage, dosage_unit: drug.dosage_unit, dose_time_name: drug.dose_time_name, }; }); const medInfo = order.medInfo && typeof order.medInfo === "object" ? order.medInfo : {}; const reviewData = { feeType: order.feeType, patientId: order.patientId, medOrgOrderNo: order.registerId, med_type: medInfo.med_type, dise_codg: medInfo.dise_codg, dise_name: medInfo.dise_name, doctorCode: order.doctorCode, unitCode: order.unitCode, doctorCode: order.doctorCode, doctorCode: order.doctorCode, diagnosisList, drugs: reviewDrugs, }; const payload = formatUploadRxData(reviewData); const { success, message } = await hnHis({ type: "ybReviewPrescription", data: payload, }); return { success: Boolean(success), message: message ? message : (success ? '查询成功' : '查询合理用药结果失败'), }; } catch (e) { return { success: false, message: `获取处方审核结果失败: ${e.message}` }; } } async function runOnlineRxExpireSchedule(db) { if (scheduleTaskIsRunning) return; scheduleTaskIsRunning = true; try { const rxList = await db.collection('diagnostic-record').find( { $or: [ { // 在线购药处方过期 prescriptionType: 'onlineMedicinePurchase',// { $in: ['onlineMedicinePurchase', 'medicalWinePurchase'] }, status: 'PASS', expireTime: { $lt: Date.now() }, medOrgOrderNo: { $exists: true }, hisChargeStatus: { $exists: false }, createTime: { $gt: dayjs().startOf('day').valueOf() } }, { // 快递的浸酒处方过期 prescriptionType: 'medicalWinePurchase', status: 'PASS', pickUpType: 'delivery', expireTime: { $lt: Date.now() }, medOrgOrderNo: { $exists: true }, hisChargeStatus: { $exists: false }, createTime: { $gt: dayjs().startOf('day').valueOf() } }, ] }, { projection: { _id: 1, medOrgOrderNo: 1, patientId: 1 } } ).toArray(); for (let rx of rxList) { const { data } = await zytHis({ type: 'getHisMedicinePurchase', patientId: rx.patientId, orderNo: rx.medOrgOrderNo }); if (data && data.pay_mark === '5') { const { success } = await zytHis({ type: 'cancelMedicinePurchase', patientId: rx.patientId, orderNo: rx.medOrgOrderNo }); if (success) { await db.collection('diagnostic-record').updateOne({ _id: rx._id }, { $set: { hisChargeStatus: 'x' } }); } } else if (data && data.pay_mark) { await db.collection('diagnostic-record').updateOne({ _id: rx._id }, { $set: { hisChargeStatus: data.pay_mark } }); } } } catch (e) { console.log(`[处方过期定时任务]: 执行失败: ${e?.message}`) return { success: false, message: `执行定时任务出现错误: ${e.message}` } } scheduleTaskIsRunning = false; } async function getPatientHomeRxCount(ctx) { try { const accountId = typeof ctx.accountId === 'string' && ctx.accountId.trim() !== '' ? ctx.accountId.trim() : ''; if (!accountId) { return { success: false, message: "账号id不能为空" }; } // 使用聚合查询实现待支付处方数量统计 const result = await db.collection("diagnostic-record").aggregate([ { // 第一步:匹配符合条件的处方 $match: { accountId: accountId, status: status.pass, prescriptionType: "onlineMedicinePurchase", expireTime: { $gt: Date.now() } } }, { // 第二步:联表查询对应的购药订单 $lookup: { from: "medicine-purchase-order", let: { rpNo: { $toString: "$_id" }, accountId: "$accountId" }, pipeline: [ { $match: { $expr: { $and: [ { $eq: ["$disabled", false] }, { $eq: ["$rpNo", "$$rpNo"] }, { $eq: ["$accountId", "$$accountId"] } ] } } }, { $project: { _id: 1, status: 1, disabled: 1 } } ], as: "purchaseOrder" } }, { // 第三步:筛选待支付处方(购药订单不存在或status为unpay) $match: { $or: [ { purchaseOrder: { $size: 0 } }, // 购药订单不存在 { $expr: { $eq: [{ $arrayElemAt: ["$purchaseOrder.status", 0] }, "unpay"] } } // 购药订单状态为unpay(取数组第一个元素的status) ] } }, { // 第四步:统计数量 $count: "count" } ]).toArray(); const unpay = result.length > 0 && result[0].count ? result[0].count : 0; const unpayDeliveryFee = await db.collection('medicine-purchase-order').countDocuments({ accountId, shippingFee: { $gt: 0 }, status: 'ybPaid' }); return { success: true, message: "查询成功", data: { unpay, unpayDeliveryFee } }; } catch (e) { return { success: false, message: `获取患者首页处方数量失败: ${e.message}` } } } async function getReUploadRxList(ctx) { try { const pharmacistNo = typeof ctx.pharmacistNo === 'string' && ctx.pharmacistNo.trim() !== '' ? ctx.pharmacistNo.trim() : ''; if (!pharmacistNo) { return { success: false, message: "药师编号不能为空" }; } const query = { prescriptionType: 'storeMedicinePurchase', pharmacistNo, needReUpload: true, // createTime: { // $gt: dayjs().startOf('day').valueOf() // } } const list = await db.collection('diagnostic-record').find(query, { projection: { _id: 1, auditTime: 1, name: 1 } }).toArray(); const total = await db.collection('diagnostic-record').countDocuments(query); return { success: true, message: "查询成功", data: list.map(i => decryptDiagnosticRecordFields(i)), total }; } catch (e) { return { success: false, message: `获取需要重新上传的处方列表失败: ${e.message}` } } } async function handleReUploadRx(ctx) { try { const pharmacistNo = typeof ctx.pharmacistNo === 'string' && ctx.pharmacistNo.trim() !== '' ? ctx.pharmacistNo.trim() : ''; const ids = Array.isArray(ctx.ids) ? ctx.ids.filter(i => typeof i == 'string' && ObjectId.isValid(i)) : []; if (!pharmacistNo || ids.length === 0) { return { success: false, message: "药师编号或处方id不能为空" }; } const { matchedCount } = await db.collection('diagnostic-record').updateMany({ _id: { $in: ids.map(i => new ObjectId(i)) }, prescriptionType: 'storeMedicinePurchase', pharmacistNo, needReUpload: true }, { $set: { needReUpload: false } }); if (matchedCount) { return { success: true, message: "操作成功" }; } return { success: false, message: "操作失败" }; } catch (e) { return { success: true, message: e.message } } } async function getPatientWineBuyCount(ctx, db) { try { const wineId = typeof ctx.wineId === 'string' && ctx.wineId.trim() !== '' ? ctx.wineId.trim() : ''; const patientId = typeof ctx.patientId === 'string' && ctx.patientId.trim() !== '' ? ctx.patientId.trim() : ''; if (!wineId || !patientId) { return { success: false, message: "酒类id或患者id号不能为空" }; } const query = { prescriptionType: 'medicalWinePurchase', "wines._id": wineId, patientId, } const count = await db.collection('diagnostic-record').countDocuments(query); return { success: true, message: "查询成功", data: count }; } catch (e) { return { success: false, message: `查询患者购买酒类次数失败: ${e.message}` } } }