feat: 对接合理用药

This commit is contained in:
huxuejian 2026-09-04 09:57:39 +08:00
parent eb58dfdbbe
commit 8125e9d4e6
5 changed files with 262 additions and 86 deletions

View File

@ -274,13 +274,13 @@ async function cancelAutoOpenForHighRisk({
} }
} }
if (shouldNotify) { // if (shouldNotify) {
await sendManualOpenNotification( // await sendManualOpenNotification(
db, // db,
{ type: AUTOMATION_TYPES.OPEN_RX, corpId, orderId }, // { type: AUTOMATION_TYPES.OPEN_RX, corpId, orderId },
doctorCode // doctorCode
); // );
} // }
return { return {
success: false, success: false,
skipped: true, skipped: true,
@ -960,6 +960,20 @@ async function handleAutoOpenRx(db, task) {
const params = await buildAutoPrescriptionParams(db, order); const params = await buildAutoPrescriptionParams(db, order);
const diagnosticRecord = require("../diagnostic-record"); 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( const result = await diagnosticRecord(
{ {
type: "addConsultDiagnosis", type: "addConsultDiagnosis",

View File

@ -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 () => { test("fails automatic audit immediately and sends the failure reason", async () => {
tencentIM.mockResolvedValue({ success: true }); tencentIM.mockResolvedValue({ success: true });
const rxId = new ObjectId(); const rxId = new ObjectId();

View File

@ -91,9 +91,9 @@ exports.formatUploadRxData = function (data) {
hospid: "108", hospid: "108",
phar_code: data.pharmacistNo, phar_code: data.pharmacistNo,
phar_name: data.pharmacist, phar_name: data.pharmacist,
phar_chk_time: dayjs(data.auditTime).format("YYYY-MM-DD HH:mm:ss"), phar_chk_time: data.auditTime ? dayjs(data.auditTime).format("YYYY-MM-DD HH:mm:ss") : "",
ysjd_time: dayjs(data.prescriptionStartTime).format("YYYY-MM-DD HH:mm:ss"), ysjd_time: data.prescriptionStartTime ? dayjs(data.prescriptionStartTime).format("YYYY-MM-DD HH:mm:ss") : "",
prsc_time: dayjs(data.prescriptionEndTime).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) => { details: data.drugs.map((i) => {
const item = { const item = {
erp_id: i.erpId, erp_id: i.erpId,

View File

@ -18,7 +18,7 @@ const consultOrder = require("../consult-order");
const tencentIM = require("../tencent-im"); const tencentIM = require("../tencent-im");
const beijingCa = require("../beijing-ca"); const beijingCa = require("../beijing-ca");
const BigNumber = require("bignumber.js"); const BigNumber = require("bignumber.js");
const timeApi = require("../hlw-time-duration"); // const timeApi = require("../hlw-time-duration");
const types = require("./types"); const types = require("./types");
/** /**
@ -218,25 +218,25 @@ async function addConsultDiagnosis(item) {
if (orderExpired) { if (orderExpired) {
return { success: false, message: "当前订单不在有效期内" }; 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 }); const doctor = await db.collection("hlw-doctor").findOne({ corpId, doctorNo: doctorCode });
if (!doctor) { if (!doctor) {
return { success: false, message: "医生不存在" }; return { success: false, message: "医生不存在" };
} }
// 判断是否医生发的消息是否大于2条 // 判断是否医生发的消息是否大于2条
const chatCount = automated // const chatCount = automated
? 3 // ? 3
: await db.collection("im-chat-msg").countDocuments({ // : await db.collection("im-chat-msg").countDocuments({
From_Account: doctorCode, // From_Account: doctorCode,
To_Account: orderId, // To_Account: orderId,
}); // });
timeData.chatCountQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); // timeData.chatCountQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
if (!automated && chatCount < 3) { // if (!automated && chatCount < 3) {
return { // return {
success: false, // success: false,
message: "请先向患者发送2条提问消息后再开方", // message: "请先向患者发送2条提问消息后再开方",
}; // };
} // }
const record = await db const record = await db
.collection("diagnostic-record") .collection("diagnostic-record")
.findOne( .findOne(
@ -2146,47 +2146,119 @@ async function getRxMedicineOrderStatus(ctx, db) {
} }
} }
async function getYbReviewResult(ctx) { async function getYbReviewResult(ctx, db) {
let { params = {} } = ctx; const params = ctx && ctx.params && typeof ctx.params === "object" ? ctx.params : {};
try { const orderId = typeof params.orderId === "string" ? params.orderId.trim() : "";
const drugs = Array.isArray(params.drugs) ? params.drugs : []; const drugs = Array.isArray(params.drugs) ? params.drugs : [];
const insuranceCodes = drugs.map(i => i.insurance_code).filter(Boolean); const diagnosisList = Array.isArray(params.diagnosisList) ? params.diagnosisList : [];
if (insuranceCodes.length === 0) { const corpId = typeof ctx.corpId === "string" ? ctx.corpId.trim() : "";
return { success: false, message: "药品列表不能为空" };
} if (!orderId) {
const { data: hisDrugs } = await zytHis({ type: 'getHisDrugInfo', insuranceCodes, ignoreAreaCode: true }); return { success: false, message: "订单号不能为空" };
const hisDrugList = Array.isArray(hisDrugs) ? hisDrugs : []; }
const validDrugs = [] if (drugs.length === 0) {
for (let i of params.drugs) { return { success: false, message: "药品信息不能为空" };
const hisDrug = hisDrugList.find(j => i.insurance_code && i.insurance_code === j.gjbm); }
if (!hisDrug) { if (diagnosisList.length === 0) {
continue; return { success: false, message: "诊断信息不能为空" };
}
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}` }
} }
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) { async function runOnlineRxExpireSchedule(db) {

View File

@ -174,7 +174,7 @@ async function addHisCustomer(item) {
*/ */
async function hlwuploadprescription(item) { async function hlwuploadprescription(item) {
try { 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"); const { code, status_code, message } = await request.main(url, params, "POST");
@ -681,39 +681,25 @@ async function uploadWinePurchaseRx(item) {
} }
} }
// hlwuploadprescription
async function ybReviewPrescription(item) { async function ybReviewPrescription(item) {
try { try {
const url = `${baseUrl}/his/hlw/ybsh`; const params = { type: "rationalDrugReview", data: item.data };
const { code, data, status_code, message } = await request.main( const { status_code, code, data, message } = await request.main(url, params, "POST");
url, if (status_code == 200 && code == '1') {
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)
return { return {
success: true, success: true,
advices, message: message,
message: "[his]医保审核成功",
}; };
} else { } else {
return { return {
success: false, success: false,
message: `[his]医保审核失败: ${message || '未知错误'}`, message: message || '查询合理用药结果失败',
}; };
} }
} catch (err) { } catch (err) {
return { return {
success: false, 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": "盒" }] } // 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); // return await syncHisChargeStatus(data);
} }