let db = ""; const { ObjectId } = require("mongodb"); const dayjs = require("dayjs"); const { decryptOrderList } = require("../consult-order/format"); const { formatUploadRxData, formatOnlineMedicinePurchaseData, formatReviewData, formatWinePurchaseData, encryptDiagnosticRecordFields, decryptDiagnosticRecordFields, decryptDiagnosticRecordList, buildHybridSearchQuery, } = require("./format"); const zytHis = require("../zyt-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"); const { getConfig } = require("../utils/config"); const PrescriptionTypeMap = { onlineMedicinePurchase: 'onlineMedicinePurchase', onlineConsult: 'storeMedicinePurchase', wineConsult: 'medicalWinePurchase' } /** * 性能优化建议: * * 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) => { 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); 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 addConsultDiagnosis(item) { let { params = {} } = item; 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 }, { projection: { createTime: 1, orderStatus: 1, registerId: 1, medorg_order_no: 1, accountId: 1, prescriptionStartTime: 1, consultType: 1, areaCode: 1, pickUpType: 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: "当前订单状态不支持开方" }; } if (!(order.createTime > dayjs().startOf("day").valueOf())) { return { success: false, message: "当前订单不在有效期内" }; } timeApi({ type: "addDoctorRxDuration", orderId }, db); // 提交诊断前, 先判单CA 自动签名是否失效 const res = await conditionalCallBeijingCa({ type: "autoSignVerifyCA", code: doctorCode, }); timeData.caSignVerifyTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); const doctorCAUserId = res.userId; if (!res.success) { return { success: false, message: "CA自动签名已失效, 请开启自动签" }; } // 判断是否医生发的消息是否大于2条 const chatCount = await db.collection("im-chat-msg").countDocuments({ From_Account: doctorCode, To_Account: orderId, }); timeData.chatCountQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); if (chatCount < 3) { return { success: false, message: "请先向患者发送2条提问消息后再开方", }; } const record = await db .collection("diagnostic-record") .findOne( { orderId, doctorCode }, { projection: { _id: 1, status: 1, pharmacistNo: 1 } } ); timeData.recordQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); if (record && record.status === status.init) { return { success: false, message: "该订单已存在诊断信息,请勿重复添加" }; } if (record && record.status === status.pass) { return { success: false, message: "该订单已审核通过,请勿重复添加" }; } if (order.consultType === "onlineMedicinePurchase") { const { success, message, drugs } = await getMedicinesWithHisChargeInfo(params.drugs, order.areaCode); if (!success) { return { success, message } } params.drugs = drugs; } if (order.consultType === "wineConsult") { const { success, message, wines } = await getWinesWithHisChargeInfo(params.wines); if (!success) { return { success, message } } params.wines = wines; params.pickUpType = order.pickUpType; } if (record && record.status === status.unpass) { return await rewriteConsultDiagnosis( params, record._id, corpId, record.pharmacistNo, timeData, order ); } // 获取推荐药师 随机获取药师 const { success, message, data: pharmacist, } = await getRecommendPharmacist({ type: order.consultType === 'wineConsult' ? 'chinese' : 'west' }); timeData.pharmacistQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); if (!success) { return { success: false, message }; } const common = require('../../common') const data = { ...params, createTime: Date.now(), status: status.init, pharmacistNo: pharmacist.pharmacistNo, pharmacist: pharmacist.pharmacistName, doctorCAUserId, sex: order.sex, age: order.age, prescriptionType: PrescriptionTypeMap[order.consultType], accountId: order.accountId, medOrgOrderNo: order.registerId || order.medorg_order_no, areaCode: order.areaCode, }; if (order.consultType === 'wineConsult') { data.medOrgOrderNo = common.generateUniqueStr('R') } // 由后端统一设置处方时间: // - 开始时间从 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() }); } } if (['onlineMedicinePurchase', 'medicalWinePurchase'].includes(data.prescriptionType)) { const { medicinePurchaseRxDuration } = await getConfig(corpId); if (Number.isInteger(medicinePurchaseRxDuration) && medicinePurchaseRxDuration >= 10) { data.expireTime = dayjs().add(medicinePurchaseRxDuration, 'minute').valueOf(); } else { return { success: false, message: "配药处方持续时长配置不正确, 请检查配置(最小10分钟)" }; } } // 加密敏感字段 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); timeData.duration = dayjs().diff(timeData.startTime, 's'); if (timeData.duration > 3) { db.collection("perf-observation").insertOne(timeData); } return { success: true, message: "新增成功", data: insertedId, doctorCAUserId, }; } catch (err) { return { success: false, message: err.message || "新增失败", }; } } // 未通过的处方 重新提交 原药师在线的话 async function rewriteConsultDiagnosis(item, _id, corpId, pharmacistNo, timeData, order) { 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, }; 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 getStoreDiagnosisRecord(item, db) { if (typeof item.storeId !== "string" || item.storeId.trim() === "") { return { success: false, message: "参数错误" }; } 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 date = item.date && dayjs(item.date).isValid() ? dayjs(item.date) : dayjs(); const createTime = { $gte: date.startOf("day").valueOf(), $lte: date.endOf("day").valueOf(), }; const query = { drugStoreNo: item.storeId, orderSource: "MATEPAD", status: status.pass, createTime, }; if (typeof item.keyword == "string" && item.keyword.trim() !== "") { // 使用混合搜索支持明文和密文数据 const nameCondition = buildHybridSearchQuery("name", item.keyword, false); const orConditions = [ { doctorName: { $regex: item.keyword.trim(), $options: "i" } }, ]; // 如果构建了name搜索条件,添加到$or中 if (nameCondition && nameCondition.$or) { orConditions.push(...nameCondition.$or); } else if (nameCondition) { orConditions.push(nameCondition); } query.$or = orConditions; } try { const total = await db .collection("diagnostic-record") .countDocuments(query); const list = await db .collection("diagnostic-record") .find(query) .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); const wineList = decryptedList.filter(i => i.prescriptionType === 'medicalWinePurchase'); if (wineList.length) { const wines = await db.collection('chinese-medicine-order').find({ rpNo: { $in: wineList.map(i => i._id.toString()) } }).toArray(); for (let item of wineList) { if (item.pickUpType === 'selfTake') { item.wineStatus = 'completed'; continue; } const wine = wines.find(i => i.rpNo === item._id.toString()); if (wine) { item.wineStatus = wine.status; continue; } if (item.expireTime > Date.now()) { item.wineStatus = 'unpay'; } else { item.wineStatus = 'expired'; } } } return { success: true, message: "查询成功", list: decryptedList, total, paegs: Math.ceil(total / pageSize), }; } 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 } = item; const timeData = { type: 'auditDiagnosis', startTime: dayjs().format('YYYY-MM-DD HH:mm:ss') }; const res = await conditionalCallBeijingCa({ type: "autoSignVerifyCA", code: pharmacistNo, }, db); timeData.caSignVerifyTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); if (!res.success) { return { success: false, message: "CA自动签名已失效, 请开启自动签" }; } const pharmacistCAUserId = res.userId; if (![status.pass, status.unpass].includes(item.status)) { return { success: false, message: "状态错误" }; } if (!pharmacistNo || !pharmacist) { 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 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 }, // { projection: { orderId: 1, _id: 1, name: 1, sex: 1, age: 1, drugs: 1, prescriptionType: 1, diagnosisList: 1 } } ).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) } }, { $set: { uploadStatus: "uploaded", status: status.pass, auditTime: Date.now(), } } ) 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 } } ) 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 }, 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) { const hisUploadFn = { storeMedicinePurchase: 'hlwuploadprescription', onlineMedicinePurchase: 'uploadMedicinePurchaseRx', medicalWinePurchase: 'uploadWinePurchaseRx', } const formatFn = { storeMedicinePurchase: formatUploadRxData, onlineMedicinePurchase: formatOnlineMedicinePurchaseData, medicalWinePurchase: formatWinePurchaseData, } try { const uploadData = formatFn[rx.prescriptionType](rx, order); const type = hisUploadFn[rx.prescriptionType]; const { success, message } = await zytHis({ type, 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: "AUDIPASS", 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 = { 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({ 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 (accountId.value == '') { return { success: false, message: "账号id不能为空" }; } const query = { accountId, status: status.pass }; if (patientId) { query.patientId = patientId; } 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) { let { params = {} } = ctx; try { const drugs = Array.isArray(params.drugs) ? params.drugs : []; const insuranceCodes = drugs.map(i => i.insurance_code).filter(Boolean); if (insuranceCodes.length === 0) { return { success: false, message: "药品列表不能为空" }; } const { data: hisDrugs } = await zytHis({ type: 'getHisDrugInfo', insuranceCodes, ignoreAreaCode: true }); const hisDrugList = Array.isArray(hisDrugs) ? hisDrugs : []; const validDrugs = [] for (let i of params.drugs) { const hisDrug = hisDrugList.find(j => i.insurance_code && i.insurance_code === j.gjbm); if (!hisDrug) { continue; } validDrugs.push({ ...i, hisChargeInfo: { drugGroupNo: hisDrug.group_no, drugId: hisDrug.charge_code, drugSerial: hisDrug.serial, drugPrice: hisDrug.pack_retprice, drugSumamt: new BigNumber(hisDrug.pack_retprice).multipliedBy(i.quantity).toNumber() } }) } if (validDrugs.length === 0) { return { success: true, message: '未能有效查询: 药品信息未找到 ', advices: [] } } params.drugs = validDrugs; const payload = formatReviewData(params) const { success, advices, message } = await zytHis({ type: 'ybReviewPrescription', data: payload }) return { success, message, advices } } 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}` } } }