From 8125e9d4e6e26dec2ca9680889368c65c6aa9dab Mon Sep 17 00:00:00 2001 From: huxuejian Date: Fri, 4 Sep 2026 09:57:39 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AF=B9=E6=8E=A5=E5=90=88=E7=90=86?= =?UTF-8?q?=E7=94=A8=E8=8D=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hlw/automation/index.js | 28 +++-- hlw/automation/index.test.js | 104 ++++++++++++++++++ hlw/diagnostic-record/format.js | 6 +- hlw/diagnostic-record/index.js | 180 ++++++++++++++++++++++---------- hlw/hn-his/index.js | 30 ++---- 5 files changed, 262 insertions(+), 86 deletions(-) diff --git a/hlw/automation/index.js b/hlw/automation/index.js index c1961ca..7cffd17 100644 --- a/hlw/automation/index.js +++ b/hlw/automation/index.js @@ -274,13 +274,13 @@ async function cancelAutoOpenForHighRisk({ } } - if (shouldNotify) { - await sendManualOpenNotification( - db, - { type: AUTOMATION_TYPES.OPEN_RX, corpId, orderId }, - doctorCode - ); - } + // if (shouldNotify) { + // await sendManualOpenNotification( + // db, + // { type: AUTOMATION_TYPES.OPEN_RX, corpId, orderId }, + // doctorCode + // ); + // } return { success: false, skipped: true, @@ -960,6 +960,20 @@ async function handleAutoOpenRx(db, task) { const params = await buildAutoPrescriptionParams(db, order); const diagnosticRecord = require("../diagnostic-record"); + const reviewResult = await diagnosticRecord( + { + type: "getYbReviewResult", + corpId: order.corpId, + params, + }, + db + ); + if (!reviewResult || !reviewResult.success) { + throw new AutomationError( + reviewResult?.message || "合理用药审核未通过" + ); + } + const result = await diagnosticRecord( { type: "addConsultDiagnosis", diff --git a/hlw/automation/index.test.js b/hlw/automation/index.test.js index d0598b4..a790db3 100644 --- a/hlw/automation/index.test.js +++ b/hlw/automation/index.test.js @@ -624,6 +624,110 @@ describe("automation task scheduling", () => { ); }); + test("blocks automatic prescription when rational-drug review fails", async () => { + tencentIM.mockResolvedValue({ success: true }); + diagnosticRecord.mockResolvedValue({ + success: false, + message: "该处方存在不合理用药", + }); + const drugId = new ObjectId(); + const taskUpdateOne = jest.fn().mockResolvedValue({ modifiedCount: 1 }); + const order = { + orderId: "order-review-failed", + corpId: "corp-1", + orderSource: "ALIPAY_MINI", + orderStatus: "processing", + doctorCode: "D001", + patientId: "P001", + name: "患者甲", + description: "鼻塞流涕", + medInfo: { dise_codg: "J00", dise_name: "普通感冒" }, + drugs: [ + { + _id: drugId.toString(), + dosage: 10, + dosage_unit: "mg", + quantity: 1, + usageName: "口服", + frequencyName: "每日两次", + unit: "盒", + }, + ], + expireTime: Date.now() + 60_000, + }; + const db = createCollectionDb({ + "hlw-config": { + findOne: jest.fn().mockResolvedValue({ autoOpenRx: true }), + find: jest.fn().mockReturnValue({ + toArray: jest.fn().mockResolvedValue([ + { key: "store-medicine-dosage-unit", list: [{ code: "MG", name: "mg" }] }, + { key: "store-medicine-frequence", list: [{ code: "BID", name: "每日两次" }] }, + { key: "store-medicine-administration", list: [{ code: "PO", name: "口服" }] }, + { key: "medicine-package-unit", list: [{ code: "盒", name: "盒" }] }, + ]), + }), + }, + "consult-order": { findOne: jest.fn().mockResolvedValue(order) }, + "diagnostic-record": { findOne: jest.fn().mockResolvedValue(null) }, + "hlw-doctor": { + findOne: jest.fn().mockResolvedValue({ + doctorNo: "D001", + signatureUrl: "doctor-sign", + onlineStatus: "online", + }), + }, + "drug-info": { + find: jest.fn().mockReturnValue({ + toArray: jest.fn().mockResolvedValue([ + { + _id: drugId, + onSale: true, + name: "测试药品", + erpId: "ERP-1", + insurance_code: "MED-1", + dosage_form: "片剂", + days: 3, + dosage_unit: "mg", + administration_method: "口服", + freq: "每日两次", + unit: "盒", + }, + ]), + }), + }, + [COLLECTION]: { updateOne: taskUpdateOne }, + }); + const task = { + _id: new ObjectId(), + type: AUTOMATION_TYPES.OPEN_RX, + corpId: "corp-1", + orderId: order.orderId, + attempts: 1, + expiresAt: Date.now() + 60_000, + }; + + await processClaimedTask(db, task); + + expect(diagnosticRecord).toHaveBeenCalledTimes(1); + expect(diagnosticRecord).toHaveBeenCalledWith( + expect.objectContaining({ + type: "getYbReviewResult", + corpId: order.corpId, + params: expect.objectContaining({ orderId: order.orderId }), + }), + db + ); + expect(taskUpdateOne).toHaveBeenCalledWith( + expect.objectContaining({ _id: task._id, status: TASK_STATUS.RUNNING }), + expect.objectContaining({ + $set: expect.objectContaining({ + status: TASK_STATUS.FAILED, + lastError: "该处方存在不合理用药", + }), + }) + ); + }); + test("fails automatic audit immediately and sends the failure reason", async () => { tencentIM.mockResolvedValue({ success: true }); const rxId = new ObjectId(); diff --git a/hlw/diagnostic-record/format.js b/hlw/diagnostic-record/format.js index 2d10339..975cbf3 100644 --- a/hlw/diagnostic-record/format.js +++ b/hlw/diagnostic-record/format.js @@ -91,9 +91,9 @@ exports.formatUploadRxData = function (data) { hospid: "108", phar_code: data.pharmacistNo, phar_name: data.pharmacist, - phar_chk_time: dayjs(data.auditTime).format("YYYY-MM-DD HH:mm:ss"), - ysjd_time: dayjs(data.prescriptionStartTime).format("YYYY-MM-DD HH:mm:ss"), - prsc_time: dayjs(data.prescriptionEndTime).format("YYYY-MM-DD HH:mm:ss"), + phar_chk_time: data.auditTime ? dayjs(data.auditTime).format("YYYY-MM-DD HH:mm:ss") : "", + ysjd_time: data.prescriptionStartTime ? dayjs(data.prescriptionStartTime).format("YYYY-MM-DD HH:mm:ss") : "", + prsc_time: data.prescriptionEndTime ? dayjs(data.prescriptionEndTime).format("YYYY-MM-DD HH:mm:ss") : "", details: data.drugs.map((i) => { const item = { erp_id: i.erpId, diff --git a/hlw/diagnostic-record/index.js b/hlw/diagnostic-record/index.js index 19b0e36..8205e7d 100644 --- a/hlw/diagnostic-record/index.js +++ b/hlw/diagnostic-record/index.js @@ -18,7 +18,7 @@ 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 timeApi = require("../hlw-time-duration"); const types = require("./types"); /** @@ -218,25 +218,25 @@ async function addConsultDiagnosis(item) { if (orderExpired) { return { success: false, message: "当前订单不在有效期内" }; } - timeApi({ type: "addDoctorRxDuration", orderId }, db); + // 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 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( @@ -2146,47 +2146,119 @@ async function getRxMedicineOrderStatus(ctx, db) { } } -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 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) { diff --git a/hlw/hn-his/index.js b/hlw/hn-his/index.js index 95a1bf0..31a3888 100644 --- a/hlw/hn-his/index.js +++ b/hlw/hn-his/index.js @@ -174,7 +174,7 @@ async function addHisCustomer(item) { */ async function hlwuploadprescription(item) { try { - const params = { type: "hlwuploadprescription", data:item.data }; + const params = { type: "hlwuploadprescription", data: item.data }; const { code, status_code, message } = await request.main(url, params, "POST"); @@ -681,39 +681,25 @@ async function uploadWinePurchaseRx(item) { } } -// hlwuploadprescription async function ybReviewPrescription(item) { try { - const url = `${baseUrl}/his/hlw/ybsh`; - const { code, data, status_code, message } = await request.main( - url, - item.data, - "POST" - ); - if (status_code == 200 && code == '1' && !message) { - const str = Buffer.from(data, 'base64'); - const dataStr = decode(str, 'gbk'); - const strData = JSON.parse(dataStr); - const result = strData && strData.output && Array.isArray(strData.output.result) ? strData.output.result.filter(Boolean) : []; - const advices = result.map(i => ({ - name: typeof i.rule_name === 'string' ? i.rule_name.trim() : '', - text: typeof i.vola_cont === 'string' ? i.vola_cont.trim() : '', - })).filter(i => i.name || i.text) + const params = { type: "rationalDrugReview", data: item.data }; + const { status_code, code, data, message } = await request.main(url, params, "POST"); + if (status_code == 200 && code == '1') { return { success: true, - advices, - message: "[his]医保审核成功", + message: message, }; } else { return { success: false, - message: `[his]医保审核失败: ${message || '未知错误'}`, + message: message || '查询合理用药结果失败', }; } } catch (err) { return { success: false, - message: `[his]医保审核错误: ${err.message || '未知错误'}`, + message: `[his]查询合理用药错误: ${err.message || '未知错误'}`, }; } } @@ -939,4 +925,4 @@ async function hisTest() { // } // const data = { "patientid": "30015424", "registerid": "h202501070833145550001", "orderno": "h202501070833145550001", "doctorcode": "10010", "unitcode": "1005000", "visit_time": "2025-12-04 13:15", "area_code": "330602", "hospid": "108", "icds": [{ "icd_code ": "R51.x00", "icd_name": "头痛", "sort": 1 }], "details": [{ "drug_type": "xy", "drug_groupno": "7X", "drug_group": "", "drug_id": "00110", "drug_serial": "01", "drug_name": "富马酸替诺福韦二吡呋酯片", "drug_spec": "300mg×30片/盒", "drug_pric": "329.41", "drug_cnt": "1", "drug_sumamt": "329.41", "supple_code": "1", "supple_name": "口服", "medc_days": "3", "drug_times": "1", "drug_comm": "", "drug_ma": "", "remark": "", "limit_flag": "0", "usage": "11", "usage_name": "每天一次(qd)", "sin_doscnt": "2.00", "sin_dosunt": "片", "unit": "盒" }] } // return await syncHisChargeStatus(data); -} \ No newline at end of file +}