feat: 功能调整

This commit is contained in:
huxuejian 2026-07-30 16:49:41 +08:00
parent ea179d3bb3
commit 7fab382756
25 changed files with 2063 additions and 589 deletions

View File

@ -56,8 +56,8 @@ CONFIG_EMS_SIGN_SECRET=ZmVRRjkyeDZnWHFnUGxkRQ==
CONFIG_EMS_SENDER_NO=1100241354944 CONFIG_EMS_SENDER_NO=1100241354944
## 震元his api ## 海宁his api
CONFIG_ZYT_HIS_BASE_URL=http://115.239.161.48:8089 CONFIG_HN_HIS_BASE_URL=http://122.225.16.74:8026
## 银联支付请求基础路径 ## 银联支付请求基础路径
CONFIG_UNION_PAY_BASE_URL=https://test-api-open.chinaums.com/v1 CONFIG_UNION_PAY_BASE_URL=https://test-api-open.chinaums.com/v1

View File

@ -111,28 +111,16 @@ async function addConsultOrder(item) {
params.corpId = corpId; params.corpId = corpId;
params.orderSource = "ALIPAY_MINI"; // 支付宝小程序 params.orderSource = "ALIPAY_MINI"; // 支付宝小程序
params.doctorCode = params.doctorCode.trim(); params.doctorCode = params.doctorCode.trim();
params.drugStoreId = 'HN000001';
if (!params.drugStoreId) {
return { success: false, message: "药房信息缺失" };
}
return await addOnlineConsultOrder(params); return await addOnlineConsultOrder(params);
} }
async function addOnlineConsultOrder(params) { async function addOnlineConsultOrder(params) {
try { try {
params.registerId = common.generateUniqueStr('GH'); params.registerId = common.generateUniqueStr('GH');
const res1 = await getConfig(params.corpId);
if (!res1) {
return { success: false, message: "未查询到配置信息" };
}
const { autoAcceptOrder, consultationDuration } = res1 || {};
if (typeof autoAcceptOrder !== 'boolean' || !(Number.isInteger(consultationDuration) && consultationDuration > 0)) {
return { success: false, message: "咨询配置错误" };
}
if (autoAcceptOrder) {
params.orderStatus = "processing";
params.expireTime = dayjs().add(consultationDuration, "minute").valueOf();
}
params.drugStoreId = 'HN000001';
if (!params.drugStoreId) {
return { success: false, message: "药房信息缺失" };
}
const storeApi = require("../drug-store"); const storeApi = require("../drug-store");
const { success, list, message } = await storeApi( const { success, list, message } = await storeApi(
{ {
@ -200,6 +188,26 @@ async function addOnlineConsultOrder(params) {
return { success: false, message: data.message }; return { success: false, message: data.message };
} }
} }
const imAccountResult = await tencentIM({
type: "importAccount",
account: params.orderId,
corpId: params.corpId,
});
if (!imAccountResult.success) {
const rawError =
imAccountResult.errorInfo ||
imAccountResult.error ||
imAccountResult.message ||
"未知错误";
const errorInfo =
typeof rawError === "string" ? rawError : JSON.stringify(rawError);
return {
success: false,
message: `咨询订单 IM 账号初始化失败:${errorInfo}`,
errorCode: imAccountResult.errorCode,
errorInfo: imAccountResult.errorInfo,
};
}
params.medorg_order_no = common.generateUniqueStr('HLW', 12); params.medorg_order_no = common.generateUniqueStr('HLW', 12);
// 加密敏感字段 // 加密敏感字段
const encryptedParams = encryptOrderFields(params); const encryptedParams = encryptOrderFields(params);
@ -207,6 +215,14 @@ async function addOnlineConsultOrder(params) {
.collection("consult-order") .collection("consult-order")
.insertOne(encryptedParams); .insertOne(encryptedParams);
if (insertedId) { if (insertedId) {
if (params.payStatus === 'success') {
await startConsultOrder(params);
}
const res1 = await getConfig(params.corpId);
const { autoAcceptOrder } = res1 || {};
if (autoAcceptOrder) {
await acceptConsultOrder(params);
}
return { return {
success: true, success: true,
message: "新增成功", message: "新增成功",
@ -647,6 +663,9 @@ async function acceptConsultOrder(item) {
) { ) {
return { success: false, message: "订单已过期,请刷新" }; return { success: false, message: "订单已过期,请刷新" };
} }
if (item.orderStatus === 'processing') {
return { success: true, message: "订单已处理" };
}
if (item.orderStatus !== "pending") { if (item.orderStatus !== "pending") {
return { success: false, message: "订单已失效,请刷新" }; return { success: false, message: "订单已失效,请刷新" };
} }
@ -917,6 +936,8 @@ async function getConsultOrder(item) {
patientId, patientId,
idCard, idCard,
orderStatus, orderStatus,
orderStatuses,
payStatus,
mergeStatus, mergeStatus,
doctorCode, doctorCode,
corpId, corpId,
@ -926,14 +947,56 @@ async function getConsultOrder(item) {
orderSource, orderSource,
accountId, accountId,
} = item; } = item;
const query = {}; const query = { corpId };
const allowedOrderStatuses = new Set([
"pending",
"processing",
"completed",
"finished",
"cancelled",
]);
const allowedPayStatuses = new Set([
"pending",
"success",
"failed",
"expired",
"cancelled",
"refunded",
"refundFailed",
]);
if (orderId) query.orderId = orderId; if (orderId) query.orderId = orderId;
if (hospitalId) query.hospitalId = hospitalId; if (hospitalId) query.hospitalId = hospitalId;
if (patientId) query.patientId = patientId; if (patientId) query.patientId = patientId;
if (idCard) query.idCard = idCard; if (idCard) query.idCard = idCard;
// if (corpId) query.corpId = corpId;
if (doctorCode) query.doctorCode = doctorCode; if (doctorCode) query.doctorCode = doctorCode;
if (orderStatus) query.orderStatus = orderStatus; if (orderStatus !== undefined && orderStatuses !== undefined) {
return { success: false, message: "订单状态参数不能重复" };
}
if (orderStatus !== undefined) {
if (
typeof orderStatus !== "string" ||
!allowedOrderStatuses.has(orderStatus)
) {
return { success: false, message: "订单状态参数错误" };
}
query.orderStatus = orderStatus;
}
if (orderStatuses !== undefined) {
if (
!Array.isArray(orderStatuses) ||
orderStatuses.length === 0 ||
orderStatuses.some((value) => !allowedOrderStatuses.has(value))
) {
return { success: false, message: "订单状态参数错误" };
}
query.orderStatus = { $in: [...new Set(orderStatuses)] };
}
if (payStatus !== undefined) {
if (typeof payStatus !== "string" || !allowedPayStatuses.has(payStatus)) {
return { success: false, message: "支付状态参数错误" };
}
query.payStatus = payStatus;
}
if (drugStoreId) query.drugStoreId = drugStoreId; if (drugStoreId) query.drugStoreId = drugStoreId;
if (orderSource) query.orderSource = orderSource; if (orderSource) query.orderSource = orderSource;
if (typeof accountId === 'string' && accountId.trim()) query.accountId = accountId.trim(); if (typeof accountId === 'string' && accountId.trim()) query.accountId = accountId.trim();
@ -959,7 +1022,7 @@ async function getConsultOrder(item) {
// 先查询所有通过审核的诊断记录的 orderId // 先查询所有通过审核的诊断记录的 orderId
const passedDiagnosticOrderIds = await db const passedDiagnosticOrderIds = await db
.collection("diagnostic-record") .collection("diagnostic-record")
.distinct("orderId", { status: "PASS" }); .distinct("orderId", { corpId, status: "PASS" });
// 根据 hasPassDiagnostic 条件调整查询 // 根据 hasPassDiagnostic 条件调整查询
if (hasPassDiagnostic) { if (hasPassDiagnostic) {
// 只查询有通过诊断的订单 // 只查询有通过诊断的订单
@ -1060,7 +1123,7 @@ async function getConsultOrder(item) {
// 仍然需要根据诊断状态过滤 // 仍然需要根据诊断状态过滤
const passedDiagnosticOrderIds = await db const passedDiagnosticOrderIds = await db
.collection("diagnostic-record") .collection("diagnostic-record")
.distinct("orderId", { status: "PASS" }); .distinct("orderId", { corpId, status: "PASS" });
if (hasPassDiagnostic) { if (hasPassDiagnostic) {
// 只查询有通过诊断的订单 // 只查询有通过诊断的订单
@ -1114,7 +1177,7 @@ async function getConsultOrder(item) {
// : query; // : query;
// console.log("allQuery case2: ", allQuery); // console.log("allQuery case2: ", allQuery);
const allQuery = query; const allQuery = query;
console.log("allQuery case3333: ", allQuery);
res = await db res = await db
.collection("consult-order") .collection("consult-order")
.find(allQuery) .find(allQuery)

View File

@ -118,4 +118,6 @@ interface DiagnosticRecord {
hisChargeStatus?: string // 配药订单处方的his支付状态 (在线购药的处方才有) hisChargeStatus?: string // 配药订单处方的his支付状态 (在线购药的处方才有)
needReUpload?: boolean // 门店处方 是否需要重新上传(可能存在的场景 调用his接口上传成功了 但是his未上传医保, 需要药师手动去his的上传处理页面上传) needReUpload?: boolean // 门店处方 是否需要重新上传(可能存在的场景 调用his接口上传成功了 但是his未上传医保, 需要药师手动去his的上传处理页面上传)
printCount?: number; // 门店自费处方打印次数
lastPrintTime?: number; // 门店自费处方最近一次打印时间
} }

View File

@ -80,8 +80,12 @@ function decryptDiagnosticRecordList(list) {
exports.formatUploadRxData = function (data) { exports.formatUploadRxData = function (data) {
return { return {
pay_type: data.feeType === 'YB' ? '1' : '0',
patientid: data.patientId, patientid: data.patientId,
orderno: data.medOrgOrderNo, orderno: data.medOrgOrderNo,
med_type: data.med_type,
dise_codg: data.dise_codg,
dise_name: data.dise_name,
doctorcode: data.doctorCode, doctorcode: data.doctorCode,
unitcode: data.unitCode, unitcode: data.unitCode,
hospid: "108", hospid: "108",
@ -92,9 +96,10 @@ exports.formatUploadRxData = function (data) {
prsc_time: dayjs(data.prescriptionEndTime).format("YYYY-MM-DD HH:mm:ss"), prsc_time: dayjs(data.prescriptionEndTime).format("YYYY-MM-DD HH:mm:ss"),
details: data.drugs.map((i) => { details: data.drugs.map((i) => {
const item = { const item = {
drug_id: i.insurance_code, erp_id: i.erpId,
drug_id: i.insurance_code || i._id,
drug_name: i.drugName, drug_name: i.drugName,
drug_dosform: "", drug_dosform: i.dosage_form,
drug_spec: i.specification || "", drug_spec: i.specification || "",
drug_pric: "0", drug_pric: "0",
drug_cnt: i.quantity, drug_cnt: i.quantity,
@ -125,79 +130,6 @@ exports.formatUploadRxData = function (data) {
}; };
exports.formatOnlineMedicinePurchaseData = function (data) {
const details = data.drugs.map(i => {
const item = {
drug_type: "xy",
drug_groupno: i.hisChargeInfo.drugGroupNo,
drug_group: "",
drug_id: i.hisChargeInfo.drugId,
drug_serial: i.hisChargeInfo.drugSerial,
drug_name: i.drugName,
drug_spec: i.specification || "",
drug_pric: i.hisChargeInfo.drugPrice,
drug_cnt: i.quantity,
drug_sumamt: i.hisChargeInfo.drugSumamt, //总金额 总金额=数量*单价
supple_code: i.usageCode,
supple_name: i.usageName,
medc_days: i.days,
drug_times: 1,
drug_comm: "",
drug_ma: "",
remark: "",
limit_flag: "0",
usage: i.frequencyCode,
usage_name: i.frequencyName,
sin_doscnt: i.dosage,
sin_dosunt: i.dosage_unit_code,
unit: i.unit,
appr_flag: i.forceSelfPay === 'Y' ? '2':'1'
}
if (item.appr_flag === '2') {
//[his要求]当appr_flag=2时必填,如果这个药品维护了限制使用范围的,传1,否则就传2
item.self_pay_rea = i.limitUsageScope === 'Y' ? '1' : '2'
}
return item
})
return {
patientid: data.patientId,
registerid: "",
orderno: data.medOrgOrderNo,
doctorcode: data.doctorCode,
unitcode: data.unitCode,
visit_time: dayjs(data.prescriptionStartTime).format("YYYY-MM-DD HH:mm:ss"),
area_code: data.areaCode,
hospid: "108",
details,
icds: data.diagnosisList.map((i, idx) => ({
icd_code: i.code,
icd_name: i.name,
sort: idx + 1
}))
}
}
exports.formatWinePurchaseData = function (data, order) {
const [wine] = data.wines;
return {
patientid: data.patientId,
registerid: order.medorg_order_no,
orderno: data.medOrgOrderNo,
doctorcode: data.doctorCode,
unitcode: data.unitCode,
visit_time: dayjs(data.prescriptionStartTime).format("YYYY-MM-DD HH:mm:ss"),
operator_id: data.drugStoreNo,
tc_code: wine.hisId,
drug_times: wine.quantity,
delivery: data.pickUpType === 'delivery' ? 1 :0,//是否配送 1配送 0自提 order.pickUpType === 'delivery' ? 1 :0
hospid: "108",
icds: data.diagnosisList.map((i) => ({
icd_code: i.code,
icd_name: i.name,
})),
};
}
/** /**
* 构建支持明文+密文混合搜索的查询条件 * 构建支持明文+密文混合搜索的查询条件
* 用于数据迁移期间,数据库中同时存在明文和密文数据的情况 * 用于数据迁移期间,数据库中同时存在明文和密文数据的情况
@ -301,7 +233,7 @@ exports.formatReviewData = function (data) {
sin_doscnt: i.dosage, sin_doscnt: i.dosage,
sin_dosunt: i.dosage_unit_code, sin_dosunt: i.dosage_unit_code,
unit: i.unit, unit: i.unit,
appr_flag: i.forceSelfPay === 'Y' ? '2':'1' appr_flag: i.forceSelfPay === 'Y' ? '2' : '1'
} }
if (item.appr_flag === '2') { if (item.appr_flag === '2') {
//[his要求]当appr_flag=2时必填,如果这个药品维护了限制使用范围的,传1,否则就传2 //[his要求]当appr_flag=2时必填,如果这个药品维护了限制使用范围的,传1,否则就传2

View File

@ -1,5 +1,6 @@
let db = ""; let db = "";
const { ObjectId } = require("mongodb"); const { ObjectId } = require("mongodb");
const { getDatabase } = require("../../mongodb");
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const { decryptOrderList } = require("../consult-order/format"); const { decryptOrderList } = require("../consult-order/format");
const { const {
@ -12,20 +13,13 @@ const {
decryptDiagnosticRecordList, decryptDiagnosticRecordList,
buildHybridSearchQuery, buildHybridSearchQuery,
} = require("./format"); } = require("./format");
const zytHis = require("../zyt-his"); const hnHis = require("../hn-his");
const consultOrder = require("../consult-order"); 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");
const { getConfig } = require("../utils/config");
const PrescriptionTypeMap = {
onlineMedicinePurchase: 'onlineMedicinePurchase',
onlineConsult: 'storeMedicinePurchase',
wineConsult: 'medicalWinePurchase'
}
/** /**
* 性能优化建议: * 性能优化建议:
@ -83,7 +77,7 @@ const status = {
invalid: "INVALID", //处方作废 (目前就订单退费会导致处方作废) invalid: "INVALID", //处方作废 (目前就订单退费会导致处方作废)
}; };
module.exports = async (item, mongodb) => { module.exports = async (item, mongodb, user) => {
db = mongodb; db = mongodb;
switch (item.type) { switch (item.type) {
case "addConsultDiagnosis": case "addConsultDiagnosis":
@ -93,7 +87,9 @@ module.exports = async (item, mongodb) => {
case "getDiagnosisById": case "getDiagnosisById":
return await getDiagnosisById(item, db); return await getDiagnosisById(item, db);
case "getStoreDiagnosisRecord": case "getStoreDiagnosisRecord":
return await getStoreDiagnosisRecord(item, db); return await getStoreDiagnosisRecord(item, db, user);
case "markStoreRxPrinted":
return await markStoreRxPrinted(item, db, user);
case "getAuditDiagnosisList": case "getAuditDiagnosisList":
return await getAuditDiagnosisList(item, db); return await getAuditDiagnosisList(item, db);
case "getAuditedDiagnosisList": case "getAuditedDiagnosisList":
@ -164,18 +160,20 @@ async function addConsultDiagnosis(item) {
const timeData = { type: 'addConsultDiagnosis', startTime: dayjs().format('YYYY-MM-DD HH:mm:ss'), orderId } const timeData = { type: 'addConsultDiagnosis', startTime: dayjs().format('YYYY-MM-DD HH:mm:ss'), orderId }
try { try {
const order = await db.collection("consult-order").findOne( const order = await db.collection("consult-order").findOne(
{ orderId }, { orderId, corpId },
{ {
projection: { projection: {
corpId: 1,
createTime: 1, createTime: 1,
orderStatus: 1, orderStatus: 1,
registerId: 1, registerId: 1,
medorg_order_no: 1, medorg_order_no: 1,
accountId: 1, accountId: 1,
prescriptionStartTime: 1, prescriptionStartTime: 1,
consultType: 1,
areaCode: 1, areaCode: 1,
pickUpType: 1, pickUpType: 1,
medInfo: 1,
feeType: 1,
sex: 1, sex: 1,
age: 1 age: 1
}, },
@ -192,15 +190,9 @@ async function addConsultDiagnosis(item) {
return { success: false, message: "当前订单不在有效期内" }; return { success: false, message: "当前订单不在有效期内" };
} }
timeApi({ type: "addDoctorRxDuration", orderId }, db); timeApi({ type: "addDoctorRxDuration", orderId }, db);
// 提交诊断前, 先判单CA 自动签名是否失效 const doctor = await db.collection("hlw-doctor").findOne({ corpId, doctorNo: doctorCode });
const res = await conditionalCallBeijingCa({ if (!doctor) {
type: "autoSignVerifyCA", return { success: false, message: "医生不存在" };
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条 // 判断是否医生发的消息是否大于2条
const chatCount = await db.collection("im-chat-msg").countDocuments({ const chatCount = await db.collection("im-chat-msg").countDocuments({
@ -228,21 +220,7 @@ async function addConsultDiagnosis(item) {
if (record && record.status === status.pass) { if (record && record.status === status.pass) {
return { success: false, message: "该订单已审核通过,请勿重复添加" }; 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) { if (record && record.status === status.unpass) {
return await rewriteConsultDiagnosis( return await rewriteConsultDiagnosis(
params, params,
@ -258,30 +236,30 @@ async function addConsultDiagnosis(item) {
success, success,
message, message,
data: pharmacist, data: pharmacist,
} = await getRecommendPharmacist({ type: order.consultType === 'wineConsult' ? 'chinese' : 'west' }); } = await getRecommendPharmacist({ corpId });
timeData.pharmacistQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); timeData.pharmacistQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
if (!success) { if (!success) {
return { success: false, message }; return { success: false, message };
} }
const common = require('../../common')
const data = { const data = {
...params, ...params,
createTime: Date.now(), createTime: Date.now(),
status: status.init, status: status.init,
pharmacistNo: pharmacist.pharmacistNo, pharmacistNo: pharmacist.pharmacistNo,
pharmacist: pharmacist.pharmacistName, pharmacist: pharmacist.pharmacistName,
doctorCAUserId, doctorSignImg: doctor.signatureUrl,
sex: order.sex, sex: order.sex,
age: order.age, age: order.age,
prescriptionType: PrescriptionTypeMap[order.consultType], // prescriptionType: PrescriptionTypeMap[order.consultType],
accountId: order.accountId, accountId: order.accountId,
medOrgOrderNo: order.registerId || order.medorg_order_no, medOrgOrderNo: order.registerId || order.medorg_order_no,
areaCode: order.areaCode, areaCode: order.areaCode,
med_type: order.medInfo.med_type,
dise_codg: order.medInfo.dise_codg,
dise_name: order.medInfo.dise_name,
feeType: order.feeType,
corpId,
}; };
if (order.consultType === 'wineConsult') {
data.medOrgOrderNo = common.generateUniqueStr('R')
}
// 由后端统一设置处方时间: // 由后端统一设置处方时间:
// - 开始时间从 consult-order 读取(接单时已由后端写入) // - 开始时间从 consult-order 读取(接单时已由后端写入)
// - 结束时间为首次提交医嘱的服务器时间 // - 结束时间为首次提交医嘱的服务器时间
@ -317,14 +295,7 @@ async function addConsultDiagnosis(item) {
await db.collection('doctor-last-submit-time').insertOne({ doctorCode: data.doctorCode, submitTime: Date.now() }); await db.collection('doctor-last-submit-time').insertOne({ doctorCode: data.doctorCode, submitTime: Date.now() });
} }
} }
if (['onlineMedicinePurchase', 'medicalWinePurchase'].includes(data.prescriptionType)) { // return { success: false, message: '不让提交', data }
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 encryptedData = encryptDiagnosticRecordFields(data);
const { insertedId } = await db const { insertedId } = await db
@ -351,7 +322,6 @@ async function addConsultDiagnosis(item) {
success: true, success: true,
message: "新增成功", message: "新增成功",
data: insertedId, data: insertedId,
doctorCAUserId,
}; };
} catch (err) { } catch (err) {
return { return {
@ -468,147 +438,94 @@ async function getOrderDiagnosis(item, db) {
} }
} }
async function getStoreDiagnosisRecord(item, db) { async function resolveStoreIds(item, db, user) {
if (typeof item.storeId !== "string" || item.storeId.trim() === "") { let authorizedStoreId =
user && typeof user.drugStoreId === "string" ? user.drugStoreId.trim() : "";
if (!authorizedStoreId && user && ObjectId.isValid(user.userId)) {
const corpDb = await getDatabase("corp");
const account = await corpDb.collection("corp-member").findOne(
{ _id: new ObjectId(user.userId) },
{ projection: { drugStoreId: 1 } }
);
authorizedStoreId =
account && typeof account.drugStoreId === "string"
? account.drugStoreId.trim()
: "";
}
if (!authorizedStoreId) {
return [];
}
const storeIds = [authorizedStoreId];
const store = await db.collection("store-list").findOne(
{ store_id: authorizedStoreId },
{ projection: { login_no: 1 } }
);
if (store && typeof store.login_no === "string" && store.login_no.trim()) {
storeIds.push(store.login_no.trim());
}
return [...new Set(storeIds)];
}
async function getStoreDiagnosisRecord(item, db, user) {
const storeIds = await resolveStoreIds(item, db, user);
if (storeIds.length === 0) {
return { success: false, message: "参数错误" }; return { success: false, message: "参数错误" };
} }
const query = {
drugStoreNo: { $in: [...new Set(storeIds)] },
status: status.pass,
};
const page = item.page > 0 && Number.isInteger(item.page) ? item.page : 1; const page = item.page > 0 && Number.isInteger(item.page) ? item.page : 1;
const pageSize = const pageSize =
item.pageSize > 0 && Number.isInteger(item.pageSize) ? item.pageSize : 20; item.pageSize > 0 && Number.isInteger(item.pageSize) ? item.pageSize : 20;
const date = const startDate = item.startDate && dayjs(item.startDate).isValid() ? dayjs(item.startDate) : null;
item.date && dayjs(item.date).isValid() ? dayjs(item.date) : dayjs(); const endDate = item.endDate && dayjs(item.endDate).isValid() ? dayjs(item.startDate) : null;
const createTime = { if (startDate) {
$gte: date.startOf("day").valueOf(), query.createTime = { $gte: startDate.startOf("day").valueOf() }
$lte: date.endOf("day").valueOf(), }
}; if (endDate) {
const query = { query.createTime = query.createTime || {};
drugStoreNo: item.storeId, query.createTime['$lte'] = endDate.endOf("day").valueOf();
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中 const nameKeyword =
if (nameCondition && nameCondition.$or) { typeof item.name === "string" ? item.name.trim().toLocaleLowerCase() : "";
orConditions.push(...nameCondition.$or); if (typeof item.medOrgOrderNo === "string" && item.medOrgOrderNo.trim()) {
} else if (nameCondition) { const keyword = item.medOrgOrderNo.trim().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
orConditions.push(nameCondition); query.medOrgOrderNo = { $regex: keyword, $options: "i" };
} }
if (["YB", "ZF"].includes(item.feeType)) {
query.$or = orConditions; query.feeType = item.feeType;
} }
try { try {
if (nameKeyword) {
// 姓名是加密字段,密文无法直接进行模糊查询。
// 先读取当前筛选范围内的姓名和 ID,解密后匹配,再用 ID 正常分页。
const nameCandidates = await db
.collection("diagnostic-record")
.find(query, { projection: { name: 1 } })
.toArray();
const matchedIds = decryptDiagnosticRecordList(nameCandidates)
.filter(
(record) =>
typeof record.name === "string" &&
record.name.toLocaleLowerCase().includes(nameKeyword)
)
.map((record) => record._id);
query._id = { $in: matchedIds };
}
const total = await db const total = await db
.collection("diagnostic-record") .collection("diagnostic-record")
.countDocuments(query); .countDocuments(query);
const list = await db const list = await db
.collection("diagnostic-record") .collection("diagnostic-record")
.find(query) .find(query)
.sort({ auditTime: -1 }) .sort({ createTime: -1, _id: -1 })
.skip((page - 1) * pageSize) .skip((page - 1) * pageSize)
.limit(pageSize) .limit(pageSize)
.toArray(); .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 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 { return {
success: true, success: true,
message: "查询成功", message: "查询成功",
@ -621,6 +538,37 @@ async function getStoreDiagnosisRecord(item, db) {
} }
} }
async function markStoreRxPrinted(item, db, user) {
const storeIds = await resolveStoreIds(item, db, user);
if (
typeof item.id !== "string" ||
!ObjectId.isValid(item.id.trim()) ||
storeIds.length === 0
) {
return { success: false, message: "参数错误" };
}
try {
const result = await db.collection("diagnostic-record").updateOne(
{
_id: new ObjectId(item.id.trim()),
drugStoreNo: { $in: [...new Set(storeIds)] },
status: status.pass,
feeType: "ZF",
},
{
$inc: { printCount: 1 },
$set: { lastPrintTime: Date.now() },
}
);
if (result.matchedCount === 0) {
return { success: false, message: "处方不存在或不可打印" };
}
return { success: true, message: "打印状态记录成功" };
} catch (e) {
return { success: false, message: e.message || "记录打印状态失败" };
}
}
async function getAuditDiagnosisList(item, db) { async function getAuditDiagnosisList(item, db) {
const page = item.page > 0 && Number.isInteger(item.page) ? item.page : 1; const page = item.page > 0 && Number.isInteger(item.page) ? item.page : 1;
const pageSize = const pageSize =
@ -951,27 +899,25 @@ async function getAuditedDiagnosisList(item, db) {
} }
async function auditDiagnosis(item, db) { async function auditDiagnosis(item, db) {
const { pharmacistNo, pharmacist } = item; // const { pharmacistNo, pharmacist, corpId } = item;
const timeData = { type: 'auditDiagnosis', startTime: dayjs().format('YYYY-MM-DD HH:mm:ss') }; const pharmacistNo = item && typeof item.pharmacistNo === 'string' ? item.pharmacistNo : '';
const res = await conditionalCallBeijingCa({ const corpId = item && typeof item.corpId === 'string' ? item.corpId : '';
type: "autoSignVerifyCA", if (!corpId || !pharmacistNo) {
code: pharmacistNo, return { success: false, message: "参数错误" };
}, 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)) { if (![status.pass, status.unpass].includes(item.status)) {
return { success: false, message: "状态错误" }; 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() !== '') : []; const ids = Array.isArray(item.ids) ? item.ids.filter(i => typeof i === 'string' && i.trim() !== '') : [];
if (ids.length === 0) { if (ids.length === 0) {
return { success: false, message: "id错误" }; return { success: false, message: "id错误" };
} }
const yaoshi = await db.collection('hlw-doctor').findOne({ corpId, doctorNo: pharmacistNo });
if (!yaoshi) {
return { success: false, message: "审方药师不存在" };
}
const pharmacist = yaoshi.doctorName;
const pharmacistCAUserId = yaoshi.signatureUrl;
const reasons = Array.isArray(item.reasons) const reasons = Array.isArray(item.reasons)
? item.reasons.filter((i) => typeof i === "string" && i.trim() !== "") ? item.reasons.filter((i) => typeof i === "string" && i.trim() !== "")
: []; : [];
@ -982,7 +928,6 @@ async function auditDiagnosis(item, db) {
// 1. 根据待审核id 查询待处理的处方记录 // 1. 根据待审核id 查询待处理的处方记录
const _rxList = await db.collection("diagnostic-record").find( const _rxList = await db.collection("diagnostic-record").find(
{ _id: { $in: ids.map((id) => new ObjectId(id)) }, status: status.init, pharmacistNo }, { _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(); ).toArray();
// 患者姓名 证件号 手机号解密 // 患者姓名 证件号 手机号解密
const rxList = _rxList.map(i => decryptDiagnosticRecordFields(i)); const rxList = _rxList.map(i => decryptDiagnosticRecordFields(i));
@ -1101,21 +1046,10 @@ async function auditDiagnosis(item, db) {
async function uploadRx2His(rx, order) { async function uploadRx2His(rx, order) {
const hisUploadFn = {
storeMedicinePurchase: 'hlwuploadprescription',
onlineMedicinePurchase: 'uploadMedicinePurchaseRx',
medicalWinePurchase: 'uploadWinePurchaseRx',
}
const formatFn = {
storeMedicinePurchase: formatUploadRxData,
onlineMedicinePurchase: formatOnlineMedicinePurchaseData,
medicalWinePurchase: formatWinePurchaseData,
}
try { try {
const uploadData = formatFn[rx.prescriptionType](rx, order); const uploadData = formatUploadRxData(rx, order);
const type = hisUploadFn[rx.prescriptionType]; const { success, message } = await hnHis({
const { success, message } = await zytHis({ type: 'hlwuploadprescription',
type,
data: uploadData data: uploadData
}) })
return { success, message, id: rx._id } return { success, message, id: rx._id }
@ -1867,7 +1801,7 @@ async function getAccountRxList(ctx) {
} }
const page = Number.isInteger(ctx.page) && ctx.page > 0 ? ctx.page : 1; 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 pageSize = Number.isInteger(ctx.pageSize) && ctx.pageSize > 0 ? ctx.pageSize : 10;
const query = { orderSource: "ALIPAY_MINI", accountId: ctx.accountId.trim(), status: status.pass }; const query = { corpId: ctx.corpId, orderSource: "ALIPAY_MINI", accountId: ctx.accountId.trim(), status: status.pass };
const [list, total] = await Promise.all([ 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").find(query, { projection: types.output.accountRxList }).sort({ createTime: -1 }).skip((page - 1) * pageSize).limit(pageSize).toArray(),
db.collection("diagnostic-record").countDocuments(query), db.collection("diagnostic-record").countDocuments(query),
@ -1875,7 +1809,7 @@ async function getAccountRxList(ctx) {
await getHisPrescriptionUploadStatus(list); await getHisPrescriptionUploadStatus(list);
// 获取处方单号 // 获取处方单号
const rpNos = list.filter(i => i.prescriptionType === 'onlineMedicinePurchase').map(i => i._id.toString()); 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(); const orderList = await db.collection("medicine-purchase-order").find({ corpId: ctx.corpId, rpNo: { $in: rpNos } }, { projection: types.output.medicinePurchaseOrder }).toArray();
return { success: true, message: "查询成功", orderList, list: decryptDiagnosticRecordList(list), total, pages: Math.ceil(total / pageSize), now: Date.now() }; return { success: true, message: "查询成功", orderList, list: decryptDiagnosticRecordList(list), total, pages: Math.ceil(total / pageSize), now: Date.now() };
} catch (e) { } catch (e) {
return { success: false, message: `获取处方记录失败:${e?.message || '未知错误 '}` }; return { success: false, message: `获取处方记录失败:${e?.message || '未知错误 '}` };

View File

@ -1,6 +1,7 @@
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const ExcelJS = require('exceljs'); const ExcelJS = require('exceljs');
const Sm4Util = require("../../utils/sm4-util"); const Sm4Util = require("../../utils/sm4-util");
const { buildHybridSearchQuery } = require("../diagnostic-record/format");
module.exports = async (item, db, res) => { module.exports = async (item, db, res) => {
switch (item.downloadType) { switch (item.downloadType) {
@ -87,6 +88,7 @@ async function downloadStream(db, res, options) {
async function downloadRx(ctx, db, res) { async function downloadRx(ctx, db, res) {
const { startDate, endDate, startAuditDate, endAuditDate } = ctx; const { startDate, endDate, startAuditDate, endAuditDate } = ctx;
const isStoreRxExport = ctx.prescriptionType === "storeMedicinePurchase";
const hasDates = startDate && dayjs(startDate).isValid() && endDate && dayjs(endDate).isValid(); const hasDates = startDate && dayjs(startDate).isValid() && endDate && dayjs(endDate).isValid();
const hasAuditDate = startAuditDate && dayjs(startAuditDate).isValid() && endAuditDate && dayjs(endAuditDate).isValid(); const hasAuditDate = startAuditDate && dayjs(startAuditDate).isValid() && endAuditDate && dayjs(endAuditDate).isValid();
const query = {}; const query = {};
@ -97,7 +99,8 @@ async function downloadRx(ctx, db, res) {
query.auditTime = { $gte: dayjs(startAuditDate).startOf('day').valueOf(), $lte: dayjs(endAuditDate).endOf('day').valueOf() }; query.auditTime = { $gte: dayjs(startAuditDate).startOf('day').valueOf(), $lte: dayjs(endAuditDate).endOf('day').valueOf() };
} }
if (typeof ctx.name === 'string' && ctx.name.trim() !== "") { if (typeof ctx.name === 'string' && ctx.name.trim() !== "") {
query.name = new RegExp(ctx.name); const nameCondition = buildHybridSearchQuery("name", ctx.name, false);
if (nameCondition) Object.assign(query, nameCondition);
} }
if (typeof ctx.doctorCode === 'string') { if (typeof ctx.doctorCode === 'string') {
query.doctorCode = ctx.doctorCode.trim(); query.doctorCode = ctx.doctorCode.trim();
@ -105,8 +108,47 @@ async function downloadRx(ctx, db, res) {
if (typeof ctx.pharmacistNo === 'string' && ctx.pharmacistNo.trim() !== "") { if (typeof ctx.pharmacistNo === 'string' && ctx.pharmacistNo.trim() !== "") {
query.pharmacistNo = ctx.pharmacistNo.trim(); query.pharmacistNo = ctx.pharmacistNo.trim();
} }
const storeIds = [ctx.storeId, ctx.storeLoginNo]
.filter(value => typeof value === "string" && value.trim())
.map(value => value.trim());
if (isStoreRxExport && typeof ctx.storeId === "string" && ctx.storeId.trim()) {
const store = await db.collection("store-list").findOne(
{ store_id: ctx.storeId.trim() },
{ projection: { login_no: 1 } }
);
if (store && typeof store.login_no === "string" && store.login_no.trim()) {
storeIds.push(store.login_no.trim());
}
}
if (storeIds.length) {
query.drugStoreNo = { $in: [...new Set(storeIds)] };
}
if (isStoreRxExport) {
query.prescriptionType = "storeMedicinePurchase";
}
if (typeof ctx.status === "string" && ctx.status.trim()) {
query.status = ctx.status.trim();
}
if (typeof ctx.medOrgOrderNo === "string" && ctx.medOrgOrderNo.trim()) {
const keyword = ctx.medOrgOrderNo.trim().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
query.medOrgOrderNo = { $regex: keyword, $options: "i" };
}
if (["YB", "ZF"].includes(ctx.feeType)) {
query.feeType = ctx.feeType;
}
const columns = [ const columns = isStoreRxExport ? [
{ header: '开具时间', key: 'createTime', width: 20 },
{ header: '费别', key: 'feeType', width: 10 },
{ header: '就诊人姓名', key: 'name', width: 18 },
{ header: '诊断', key: 'diagnosisList', width: 24 },
{ header: '开具药品', key: 'drugs', width: 36 },
{ header: '流转状态', key: 'transferStatus', width: 14 },
{ header: '处方编号', key: 'medOrgOrderNo', width: 24 },
{ header: '开方医生', key: 'doctorName', width: 14 },
{ header: '药品类型', key: 'drugType', width: 12 },
{ header: '打印状态', key: 'printStatus', width: 12 },
] : [
{ header: '状态', key: 'status', width: 10 }, { header: '状态', key: 'status', width: 10 },
{ header: '原因', key: 'reasons', width: 10 }, { header: '原因', key: 'reasons', width: 10 },
{ header: '提交时间', key: 'createTime', width: 20 }, { header: '提交时间', key: 'createTime', width: 20 },
@ -117,7 +159,7 @@ async function downloadRx(ctx, db, res) {
{ header: '诊断', key: 'diagnosisList', width: 10 }, { header: '诊断', key: 'diagnosisList', width: 10 },
{ header: '药品', key: 'drugs', width: 10 }, { header: '药品', key: 'drugs', width: 10 },
]; ];
let filename = '处方记录'; let filename = isStoreRxExport ? '门店处方记录' : '处方记录';
if (hasDates) { if (hasDates) {
filename += `_${dayjs(startDate).format('YYYY-MM-DD')}_${dayjs(endDate).format('YYYY-MM-DD')}`; filename += `_${dayjs(startDate).format('YYYY-MM-DD')}_${dayjs(endDate).format('YYYY-MM-DD')}`;
} else if (hasAuditDate) { } else if (hasAuditDate) {
@ -139,10 +181,21 @@ async function downloadRx(ctx, db, res) {
collection: 'diagnostic-record', collection: 'diagnostic-record',
aggregateList: [ aggregateList: [
{ $match: query }, { $match: query },
{ $project: { _id: 0, status: 1, createTime: 1, reasons: 1, auditTime: 1, name: 1, doctorName: 1, pharmacist: 1, diagnosisList: 1, drugs: 1, orderId: 1, specification: 1 } }, { $project: { _id: 0, status: 1, createTime: 1, reasons: 1, auditTime: 1, name: 1, sex: 1, age: 1, doctorName: 1, pharmacist: 1, diagnosisList: 1, drugs: 1, wines: 1, orderId: 1, medOrgOrderNo: 1, feeType: 1, hisUploadStatus: 1, prescriptionType: 1, printCount: 1 } },
{ $sort: { createTime: -1 } }, { $sort: { createTime: -1 } },
], ],
format: i => ({ format: i => isStoreRxExport ? {
createTime: i.createTime ? dayjs(i.createTime).format("YYYY-MM-DD HH:mm:ss") : "",
feeType: i.feeType === "YB" ? "医保" : "自费",
name: [Sm4Util.decryptDataForSm3(i.name) || i.name, i.sex, i.age > 0 ? `${i.age}岁` : ""].filter(Boolean).join(" "),
diagnosisList: Array.isArray(i.diagnosisList) ? i.diagnosisList.map(d => d.name || '').join("、") : "",
drugs: Array.isArray(i.drugs) ? i.drugs.map(d => d.drugName || "").filter(Boolean).join("、") : "",
transferStatus: i.feeType !== "YB" ? "-" : i.hisUploadStatus === "uploaded" ? "流转完成" : i.hisUploadStatus === "error" ? "流转失败" : "流转中",
medOrgOrderNo: i.medOrgOrderNo || "",
doctorName: i.doctorName || "",
drugType: i.prescriptionType === "medicalWinePurchase" ? "中药" : "西药",
printStatus: Number(i.printCount) > 0 ? "已打印" : "未打印",
} : ({
status: rxStatusMap[i.status] || i.status, status: rxStatusMap[i.status] || i.status,
reasons: Array.isArray(i.reasons) ? i.reasons.join(",") : "", reasons: Array.isArray(i.reasons) ? i.reasons.join(",") : "",
createTime: i.createTime ? dayjs(i.createTime).format("YYYY-MM-DD HH:mm:ss") : "", createTime: i.createTime ? dayjs(i.createTime).format("YYYY-MM-DD HH:mm:ss") : "",
@ -151,7 +204,7 @@ async function downloadRx(ctx, db, res) {
pharmacist: i.pharmacist, pharmacist: i.pharmacist,
name: Sm4Util.decryptDataForSm3(i.name)|| i.name, name: Sm4Util.decryptDataForSm3(i.name)|| i.name,
diagnosisList: Array.isArray(i.diagnosisList) ? i.diagnosisList.map(d => d.name || '').join(",") : "", diagnosisList: Array.isArray(i.diagnosisList) ? i.diagnosisList.map(d => d.name || '').join(",") : "",
drugs: Array.isArray(i.drugs) ? i.drugs.map(d => `${d.drugName} ${i.specification || ''} (${d.dosage}${d.dosage_unit}, ${d.frequencyName}, ${d.days}天)`).join(",") : "", drugs: Array.isArray(i.drugs) ? i.drugs.map(d => `${d.drugName} ${d.specification || ''} (${d.dosage}${d.dosage_unit}, ${d.frequencyName}, ${d.days}天)`).join(",") : "",
}) })
} }
await downloadStream(db, res, options); await downloadStream(db, res, options);

View File

@ -6,8 +6,7 @@ interface Drug {
_id: ObjectId; // 数据库中的唯一标识符 _id: ObjectId; // 数据库中的唯一标识符
pinyin_code: string; // 拼音码 pinyin_code: string; // 拼音码
name: string; // 药品名称 name: string; // 药品名称
product_id?: number; // 产品ID erpId: string; // ERP ID
product_id_str?: string; // 产品ID的字符串形式
specification: string; // 药品规格 specification: string; // 药品规格
package_amount: number; // 包装数量 package_amount: number; // 包装数量
manufacturer: string; // 生产厂家 manufacturer: string; // 生产厂家

View File

@ -3,6 +3,7 @@ const { ObjectId } = require("mongodb");
const { getDrugList } = require('./verify'); const { getDrugList } = require('./verify');
let db = ""; let db = "";
module.exports = async (item, mongodb) => { module.exports = async (item, mongodb) => {
db = mongodb; db = mongodb;
switch (item.type) { switch (item.type) {
@ -54,6 +55,7 @@ async function getDrugInfo(item) {
{ generic_name: new RegExp(keyword.trim(), "i") }, { generic_name: new RegExp(keyword.trim(), "i") },
{ barcode: keyword.trim() }, { barcode: keyword.trim() },
{ pinyin_code: new RegExp(keyword.trim(), "i") }, { pinyin_code: new RegExp(keyword.trim(), "i") },
{ erpId: keyword.trim() },
{ product_id_str: keyword.trim() }, { product_id_str: keyword.trim() },
]; ];
fuzzyQuery = { $or: arr }; fuzzyQuery = { $or: arr };
@ -128,7 +130,7 @@ async function updateDrugInfo(item) {
//新增药品信息 //新增药品信息
/** /**
* pinyin_code:拼音操作码 * pinyin_code:拼音操作码
product_id:药店货品ID erpId:ERP ID
name:名称 name:名称
specification:规格 specification:规格
manufacturer:厂家 manufacturer:厂家
@ -177,8 +179,11 @@ async function getHlwDrugInfo(ctx) {
if (typeof ctx.onSale === "boolean") { if (typeof ctx.onSale === "boolean") {
query.onSale = ctx.onSale query.onSale = ctx.onSale
} }
if (typeof ctx.productId === 'string' && ctx.productId.trim()) { if (typeof ctx.erpId === 'string' && ctx.erpId.trim()) {
query.product_id_str = ctx.productId.trim() query.$or = [
{ erpId: ctx.erpId.trim() },
{ product_id_str: ctx.erpId.trim() }
]
} }
if (typeof ctx.barcode === 'string' && ctx.barcode.trim()) { if (typeof ctx.barcode === 'string' && ctx.barcode.trim()) {
query.barcode = ctx.barcode.trim() query.barcode = ctx.barcode.trim()
@ -239,6 +244,20 @@ async function editHlwDrugInfo(ctx) {
} }
try { try {
const { onSale, ...data } = params; const { onSale, ...data } = params;
const currentDrug = await db.collection("drug-info").findOne(
{ _id: new ObjectId(id) },
{ projection: { erpId: 1, product_id: 1, product_id_str: 1 } }
);
if (!currentDrug) {
return { success: false, message: "未找到对应药品" }
}
const legacyErpId = currentDrug.product_id_str || currentDrug.product_id;
const erpId = Object.prototype.hasOwnProperty.call(data, "erpId")
? data.erpId
: (currentDrug.erpId || (legacyErpId || legacyErpId === 0 ? String(legacyErpId) : ""));
if (typeof erpId !== "string" || !erpId.trim()) {
return { success: false, message: "ERP ID不能为空" }
}
let updateData = {}; let updateData = {};
if (Object.keys(data).length) { if (Object.keys(data).length) {
const peopleInfo = { const peopleInfo = {
@ -366,7 +385,12 @@ async function getStoreSimilarDrugInfo(ctx) {
})) }))
similarDrugs = await db.collection("drug-info").find({ $or: similarQuery }).toArray(); similarDrugs = await db.collection("drug-info").find({ $or: similarQuery }).toArray();
} }
return { success: true, message: "查询成功", drugs: medicines, similarDrugs } return {
success: true,
message: "查询成功",
drugs: medicines,
similarDrugs: similarDrugs
}
} catch (e) { } catch (e) {
return { success: false, message: e.message } return { success: false, message: e.message }
} }
@ -382,6 +406,7 @@ async function searchDrugs(ctx) {
$or: [ $or: [
{ name: { $regex: keyword, $options: "i" } }, { name: { $regex: keyword, $options: "i" } },
{ pinyin_code: keyword }, { pinyin_code: keyword },
{ erpId: keyword },
{ product_id_str: keyword }, { product_id_str: keyword },
{ barcode: keyword }, { barcode: keyword },
{ insurance_code: keyword } { insurance_code: keyword }

View File

@ -16,7 +16,8 @@ const drugFieldDict = [
{ name: 'ERP编码', field: 'his_drug_code', verify: (val, label) => validator.verifyString(val, label) }, { name: 'ERP编码', field: 'his_drug_code', verify: (val, label) => validator.verifyString(val, label) },
{ name: '药品分类', field: 'category', verify: (val, label) => validator.verifyListItem(val, categoryList, false, label) }, { name: '药品分类', field: 'category', verify: (val, label) => validator.verifyListItem(val, categoryList, false, label) },
{ name: '药品拼音码', field: 'pinyin_code', verify: (val, label) => validator.verifyString(val, label) }, { name: '药品拼音码', field: 'pinyin_code', verify: (val, label) => validator.verifyString(val, label) },
{ name: '药库货品ID', field: 'product_id', verify: (val, label) => validateNumber(val, label) }, { name: 'ERP ID', field: 'erpId', required: true, libType: 'hlw', verify: (val, label) => validator.verifyString(val, label, 1) },
{ name: '药库货品ID', field: 'product_id', libType: 'online', verify: (val, label) => validateNumber(val, label) },
{ name: '通用名', field: 'generic_name', verify: (val, label) => validator.verifyString(val, label) }, { name: '通用名', field: 'generic_name', verify: (val, label) => validator.verifyString(val, label) },
{ name: '通用名拼音码', field: 'generic_pinyin', verify: (val, label) => validator.verifyString(val, label) }, { name: '通用名拼音码', field: 'generic_pinyin', verify: (val, label) => validator.verifyString(val, label) },
// 仅在线药品库使用:管理分类(处方药 / OTC)与剂型 // 仅在线药品库使用:管理分类(处方药 / OTC)与剂型
@ -47,9 +48,10 @@ function validateNumber(val, label, len = 0, required = false) {
return validator.verifyNumber(val, label, len) return validator.verifyNumber(val, label, len)
} }
function getDrugList(arr, verifyEveryField = true, extraInfo = {}) { function getDrugList(arr, verifyEveryField = true, extraInfo = {}, libType = 'hlw') {
const fieldDict = drugFieldDict.filter(item => !item.libType || item.libType === libType);
return arr.map(drug => { return arr.map(drug => {
const res = drugFieldDict.reduce((obj, item) => { const res = fieldDict.reduce((obj, item) => {
const key = item.field; const key = item.field;
if (!verifyEveryField && !(key in drug)) { if (!verifyEveryField && !(key in drug)) {
return obj return obj
@ -64,7 +66,7 @@ function getDrugList(arr, verifyEveryField = true, extraInfo = {}) {
} }
return obj return obj
}, { msgs: [] }) }, { msgs: [] })
if ('product_id' in res) { if (libType === 'online' && 'product_id' in res) {
res.product_id_str = res.product_id.toString(); res.product_id_str = res.product_id.toString();
} }
if ('frequency' in res) { if ('frequency' in res) {
@ -74,12 +76,12 @@ function getDrugList(arr, verifyEveryField = true, extraInfo = {}) {
}) })
} }
function getDrugFromChineseKeyObj(data) { function getDrugFromChineseKeyObj(data, libType = 'hlw') {
const rowData = {}; const rowData = {};
Object.keys(data).forEach(key => { Object.keys(data).forEach(key => {
rowData[key.trim()] = data[key]; rowData[key.trim()] = data[key];
}) })
return drugFieldDict.reduce((obj, item) => { return drugFieldDict.filter(item => !item.libType || item.libType === libType).reduce((obj, item) => {
obj[item.field] = rowData[item.name] ?? ''; obj[item.field] = rowData[item.name] ?? '';
return obj; return obj;
}, {}); }, {});

View File

@ -17,6 +17,7 @@ interface HlwDoctor {
job: Job; job: Job;
offlineTime?: number; // 上次下线时间 offlineTime?: number; // 上次下线时间
testDoctor: boolean; // 是否为测试医生 testDoctor: boolean; // 是否为测试医生
signatureUrl?: string; // 医生签名图片地址
openWestPres: boolean; // 是否开启西药处方 openWestPres: boolean; // 是否开启西药处方
openChinesePres: boolean; // 是否开启中药处方 openChinesePres: boolean; // 是否开启中药处方
} }

View File

@ -69,6 +69,10 @@ const rules = [
// 是否开通视频问诊功能:允许 true / false,默认 false // 是否开通视频问诊功能:允许 true / false,默认 false
validate: val => validator.verifyListItem(val, [true, false], false, '视频问诊功能', false) validate: val => validator.verifyListItem(val, [true, false], false, '视频问诊功能', false)
}, },
{
key: 'signatureUrl',
validate: val => validator.verifyString(val, '签名图片', 0, 500)
},
{ {
key: 'openWestPres', key: 'openWestPres',
validate: val => validator.verifyListItem(val, [true, false], false, '西医处方', false) validate: val => validator.verifyListItem(val, [true, false], false, '西医处方', false)

View File

@ -598,10 +598,7 @@ async function setPharmacistOnlineStatus(ctx) {
if (res.matchedCount === 0) { if (res.matchedCount === 0) {
return { success: false, message: "药师不存在或不是药师" } return { success: false, message: "药师不存在或不是药师" }
} }
if (res.modifiedCount === 1) { return { success: true, message: "在线状态修改成功", data: ctx.onlineStatus }
return { success: true, message: "在线状态修改成功", data: ctx.onlineStatus }
}
return { success: false, message: "在线状态修改失败" }
} catch (e) { } catch (e) {
return { return {
success: false, success: false,

View File

@ -1,6 +1,7 @@
const { ObjectId } = require('mongodb'); const { ObjectId } = require('mongodb');
const validator = require('../../utils/validator'); const validator = require('../../utils/validator');
const Sm4Util = require('../../utils/sm4-util'); const Sm4Util = require('../../utils/sm4-util');
const hnHis = require('../hn-his');
const COLLECTION_NAME = 'hlw-patient'; const COLLECTION_NAME = 'hlw-patient';
@ -55,6 +56,12 @@ function validatePatient(item) {
return { return {
success: true, success: true,
origin: {
name,
certNo,
mobile,
address,
},
data: { data: {
accountId: accountResult.accountId, accountId: accountResult.accountId,
anotherIdNo: Sm4Util.encryptDataForSm3(certNo), anotherIdNo: Sm4Util.encryptDataForSm3(certNo),
@ -78,6 +85,16 @@ async function addHlwPatient(item, db) {
if (exists > 0) { if (exists > 0) {
return { success: false, message: '当前账户下已存在相同证件号的就诊人' }; return { success: false, message: '当前账户下已存在相同证件号的就诊人' };
} }
console.log(validation.data)
const res = await hnHis({
type: 'addHisCustomer',
name: validation.origin.name,
idCard: validation.origin.certNo,
mobile: validation.origin.mobile,
address: validation.origin.address,
})
if (!res.success) return res;
const result = await db.collection(COLLECTION_NAME).insertOne({ const result = await db.collection(COLLECTION_NAME).insertOne({
...validation.data, ...validation.data,

View File

@ -1,185 +0,0 @@
process.env.CONFIG_ZYB_YB_APPID = '1234567890123456';
process.env.CONFIG_ZYB_YB_SECRET = 'hlw-patient-test-secret';
const { ObjectId } = require('mongodb');
const patientApi = require('./index');
function matches(document, query) {
return Object.entries(query).every(([key, value]) => {
if (key === '_id') return document._id.toString() === value.toString();
return document[key] === value;
});
}
function createDb() {
const documents = [];
return {
documents,
collection: jest.fn(() => ({
insertOne: async document => {
const insertedId = new ObjectId();
documents.push({ _id: insertedId, ...document });
return { insertedId };
},
countDocuments: async query => documents.filter(document => matches(document, query)).length,
find: query => ({
sort: () => ({
toArray: async () => documents.filter(document => matches(document, query)),
}),
}),
findOne: async query => documents.find(document => matches(document, query)) || null,
updateOne: async (query, update) => {
const document = documents.find(item => matches(item, query));
if (!document) return { matchedCount: 0, modifiedCount: 0 };
Object.assign(document, update.$set);
return { matchedCount: 1, modifiedCount: 1 };
},
})),
};
}
describe('hlw-patient', () => {
test('新增、查询、更新并软删除就诊人', async () => {
const db = createDb();
const patient = {
accountId: 'account-1',
name: '张三',
certNo: '330101199001011234',
mobile: '13800138000',
};
const added = await patientApi.main({ type: 'addHlwPatient', ...patient }, db);
expect(added.success).toBe(true);
expect(db.documents[0]).toMatchObject({
accountId: 'account-1',
address: '',
disabled: false,
});
expect(db.documents[0].anotherName).not.toBe(patient.name);
expect(db.documents[0].anotherIdNo).not.toBe(patient.certNo);
expect(db.documents[0].anotherMobile).not.toBe(patient.mobile);
expect(db.documents[0].createTime).toBeInstanceOf(Date);
const queried = await patientApi.main({
type: 'getHlwPatientList',
accountId: 'account-1',
}, db);
expect(queried.data).toEqual([expect.objectContaining(patient)]);
const detail = await patientApi.main({
type: 'getHlwPatient',
accountId: 'account-1',
id: added.id,
}, db);
expect(detail).toMatchObject({
success: true,
data: patient,
});
const otherAccountDetail = await patientApi.main({
type: 'getHlwPatient',
accountId: 'account-2',
id: added.id,
}, db);
expect(otherAccountDetail.success).toBe(false);
const updated = await patientApi.main({
type: 'updateHlwPatient',
...patient,
id: added.id,
name: '李四',
mobile: '13900139000',
address: '杭州市',
}, db);
expect(updated.success).toBe(true);
const updatedList = await patientApi.main({
type: 'getHlwPatientList',
accountId: 'account-1',
}, db);
expect(updatedList.data[0]).toMatchObject({
name: '李四',
mobile: '13900139000',
certNo: patient.certNo,
address: '杭州市',
});
const deleted = await patientApi.main({
type: 'deleteHlwPatient',
accountId: 'account-1',
id: added.id,
}, db);
expect(deleted.success).toBe(true);
expect(db.documents[0].disabled).toBe(true);
const emptyList = await patientApi.main({
type: 'getHlwPatientList',
accountId: 'account-1',
}, db);
expect(emptyList.data).toEqual([]);
});
test('拒绝格式错误的手机号', async () => {
const db = createDb();
const result = await patientApi.main({
type: 'addHlwPatient',
accountId: 'account-1',
name: '张三',
certNo: '330101199001011234',
mobile: '12345',
}, db);
expect(result.success).toBe(false);
expect(db.documents).toHaveLength(0);
});
test('同一账户下不能新增相同证件号的就诊人', async () => {
const db = createDb();
const patient = {
type: 'addHlwPatient',
accountId: 'account-1',
name: '张三',
certNo: '330101199001011234',
mobile: '13800138000',
};
const first = await patientApi.main(patient, db);
const duplicate = await patientApi.main({
...patient,
name: '李四',
mobile: '13900139000',
}, db);
expect(first.success).toBe(true);
expect(duplicate).toEqual({
success: false,
message: '当前账户下已存在相同证件号的就诊人',
});
expect(db.documents).toHaveLength(1);
const otherAccount = await patientApi.main({
...patient,
accountId: 'account-2',
}, db);
expect(otherAccount.success).toBe(true);
expect(db.documents).toHaveLength(2);
});
test('自动新增接口对同一账号和证件号保持幂等', async () => {
const db = createDb();
const request = {
type: 'autoAddHlwPatient',
accountId: 'account-1',
name: '张三',
certNo: '330101199001011234',
mobile: '13800138000',
};
const first = await patientApi.main(request, db);
const second = await patientApi.main(request, db);
expect(first).toMatchObject({ success: true, data: true });
expect(second).toMatchObject({ success: true, data: true });
expect(db.documents).toHaveLength(1);
});
});

942
hlw/hn-his/index.js Normal file
View File

@ -0,0 +1,942 @@
const dayjs = require("dayjs");
const { decode } = require('iconv-lite');
const request = require("../../request");
// const baseUrl = "http://115.239.161.48:8089"; //his说 8089可能会关闭,用8083
const baseUrl = process.env.CONFIG_HN_HIS_BASE_URL;
const url = `${baseUrl}/getYoucanData/hlw`;
const AppWin = {
line: '线上挂号',
win: '线下挂号',
}
module.exports = async (item) => {
switch (item.type) {
case "getHisCustomer":
return await getHisCustomer(item);
case "addHisCustomer":
return await addHisCustomer(item);
case "registration":
return await registration(item);
case "getPayStatus":
return await getPayStatus(item);
case "hlwRefund":
return await hlwRefund(item);
case "hlwuploadprescription":
return await hlwuploadprescription(item);
case "hlwPrescriptionOrderStatus":
return await hlwPrescriptionOrderStatus(item);
case "getHisPrescriptionUploadStatus":
return await getHisPrescriptionUploadStatus(item);
case "getHisStoreRegList":
return await getHisStoreRegList(item);
case "insurancePreSettle":
return await insurancePreSettle(item);
case "insuranceSettle":
return await insuranceSettle(item);
case "insuranceRefund":
return await insuranceRefund(item);
case "getRecent90daysDrugRecord":
return await getRecent90daysDrugRecord(item);
case "uploadMedicinePurchaseRx":
return await uploadMedicinePurchaseRx(item);
case "uploadWinePurchaseRx":
return await uploadWinePurchaseRx(item);
case "getHisDrugInfo":
return await getHisDrugInfo(item);
case "getHisMedicinePurchase":
return await getHisMedicinePurchase(item);
case "cancelMedicinePurchase":
return await cancelMedicinePurchase(item);
case "refundMedicinePurchase":
return await refundMedicinePurchase(item);
case "getHisInvoice":
return await getHisInvoice(item);
case "updateMedicinePurchaseDelivery":
return await updateMedicinePurchaseDelivery(item);
case 'syncHisChargeStatus':
return await syncHisChargeStatus(item);
case "hisTest":
return await hisTest(item);
case "getHisEmployeeInfo":
return await getHisEmployeeInfo(item);
case "syncHisRefundStatus":
return await syncHisRefundStatus(item);
case "getWineDrugStockInfo":
return await getWineDrugStockInfo(item);
case "ybReviewPrescription":
return await ybReviewPrescription(item);
}
};
async function getHisCustomer(ctx) {
const idCard = typeof ctx.idCard === 'string' ? ctx.idCard.trim() : '';
if (!idCard) {
return { success: false, message: '证件号不能为空' }
}
try {
const params = { type: "getHisCustomer", idCard };
const { data, status_code, message } = await request.main(url, params, "POST");
if (status_code == 200 && !message) {
return {
success: true,
list: data?.patients,
patient: data?.patients?.[0],
tmbxx: Array.isArray(data?.tmbxx) ? data?.tmbxx : [],
message: `[his]查询档案:获取his档案成功`,
};
} else {
return {
success: false,
message: message || "获取失败",
};
}
} catch (err) {
return {
success: false,
message: err.message || "获取失败",
};
}
}
/**
*
* @param {buildType} 建档类型 1.自费建档 2.电子凭证建档 3.线下医保建档
* @returns
*/
async function addHisCustomer(item) {
// let { idCard, name, mobile, address, psnToken, buildType = '1', mdtrt_cert_type, mdtrt_cert_no, card_sn } = item;
try {
const name = typeof item.name === 'string' ? item.name.trim() : '';
const idCard = typeof item.idCard === 'string' ? item.idCard.trim() : '';
const mobile = typeof item.mobile === 'string' ? item.mobile.trim() : '';
const address = typeof item.address === 'string' ? item.address.trim() : '';
const psnToken = typeof item.psnToken === 'string' ? item.psnToken.trim() : '';
const mdtrt_cert_no = typeof item.mdtrt_cert_no === 'string' ? item.mdtrt_cert_no.trim() : '';
const card_sn = typeof item.card_sn === 'string' ? item.card_sn.trim() : '';
if (!name || !idCard || !mobile) {
return { success: false, message: '姓名、证件号、手机号不能为空' }
}
const payload = {
type: 'addHisCustomer',
name,
idCard,
mobile,
address,
buildType: '1', // 1.自费建档 2.电子凭证建档 3.线下医保
};
// 电子凭证建档 医保插件授权建档
if (psnToken) {
payload.psnToken = psnToken;
payload.buildType = "2";
// payload.mdtrt_cert_type = '01'
}
// 线下医保建档
// mdtrt_cert_type 就诊凭证类型 01 | 03
// mdtrt_cert_no 就诊凭证号码 mdtrt_cert_type为01 填写电子凭证令牌 | mdtrt_cert_type为03 填写社会保障卡卡号
if (mdtrt_cert_no) {
payload.mdtrt_cert_type = '03';
payload.mdtrt_cert_no = mdtrt_cert_no;
payload.card_sn = card_sn; //就诊凭证类型为“03”时填写社会保障卡内码
}
const { data, status_code, message } = await request.main(
url,
payload,
"POST"
);
if (status_code == 200 && !message) {
return {
success: true,
list: data?.patients,
tmbxx: Array.isArray(data?.tmbxx) ? data?.tmbxx : [],
message: "获取成功",
};
} else {
return {
success: false,
message: message || "获取失败",
};
}
} catch (err) {
return {
success: false,
message: "获取失败",
};
}
}
// 6.外配处方回写
/**
*
* @param {*} param0
* @returns
*/
async function hlwuploadprescription(item) {
try {
const params = { type: "hlwuploadprescription", data:item.data };
const { code, status_code, message } = await request.main(url, params, "POST");
if (status_code == 200 && code == '1') {
return {
success: true,
message: "[his]处方上传成功",
};
} else {
return {
success: false,
message: `[his]处方上传失败: ${message || '未知错误'}`,
};
}
} catch (err) {
return {
success: false,
message: `[his]处方上传错误: ${err.message || '未知错误'}`,
};
}
}
// 预约挂号
async function registration(item) {
const { patientId, doctorCode, appwin = 'line', unitCode, registerId, chargecode, reg_fee, shopid, shopname, op_id = '', channel } = item;
if (AppWin[appwin] == undefined) {
return {
success: false,
message: "appwin参数错误"
}
}
if (AppWin[appwin] == '线下挂号' && (typeof op_id !== 'string' || op_id.trim() === '')) {
return {
success: false,
message: "op_id参数错误"
}
}
try {
const url = `${baseUrl}/his/hlw/registration`;
// 根据dayjs 判断是上午 还是下午
const ampm = dayjs().hour() < 12 ? "a" : "p";
const requestday = dayjs().format("YYYY-MM-DD HH:mm:ss");
// registerId 为 H开头 15位随机数
const payload = {
patientId,
doctorCode,
unitCode,
requestday,
ampm,
registerId,
chargecode,
reg_fee,
shopid,
shopname,
chargetype: "普通",
appwin
};
if (AppWin[appwin] == '线下挂号') {
payload.op_id = op_id;
}
if (channel === 1) {
payload.channel = 1
}
const { data, status_code, message } = await request.main(
url,
payload,
"POST"
);
if (data && data.register && data.register.medorg_order_no) {
return {
success: true,
data,
message: message || "挂号成功",
};
} else {
return {
success: false,
message: message || "挂号失败",
};
}
} catch (err) {
return {
success: false,
message: err.message || "挂号失败"
};
}
}
//订单支付状态查询
async function getPayStatus(item) {
const { patientId, registerId, medorgOrderNo } = item;
try {
const url = `${baseUrl}/his/hlw/settlement`;
const { status, status_code, message, ...rest } = await request.main(
url,
{
patientid: patientId,
registerid: registerId,
medorg_order_no: medorgOrderNo,
},
"POST"
);
const settlement =
rest.data && rest.data.settlement ? rest.data.settlement : null;
//settlement.status 0 已收费 1 已退费 5 未收费 x 已作废
if (status_code == 200) {
return {
success: true,
status: settlement ? settlement.status : '',
settlement,
message: "订单查询成功",
};
} else {
return {
success: false,
message: message || "获取失败",
};
}
} catch (err) {
return {
success: false,
message: "获取失败",
};
}
}
//医保患者退费
async function hlwRefund(item) {
const { patientId, registerId, medorgOrderNo } = item;
try {
const url = `${baseUrl}/his/hlw/refund`;
const { data, status_code, message, ...rest } = await request.main(
url,
{
patientid: patientId,
registerid: registerId,
medorg_order_no: medorgOrderNo,
},
"POST"
);
if (status_code == 200 && !message) {
return {
success: true,
message: "退费成功",
};
} else {
return {
success: false,
message: message || "退费失败",
};
}
} catch (err) {
return {
success: false,
message: "退费失败",
};
}
}
// 外配处方状态查询
async function hlwPrescriptionOrderStatus(item) {
const { patientId, orderno } = item;
try {
const url = `${baseUrl}/his/hlw/orderstatus`;
const { status_code, message, data } = await request.main(
url,
{
patientid: patientId,
orderno,
},
"POST"
);
if (status_code == 200 && data && data.orderstatus) {
return {
success: true,
uploaded: data.orderstatus.status == '1',
status: data.orderstatus.status,
message: "查询成功",
};
} else {
return {
success: false,
message: message || "查询失败",
};
}
} catch (err) {
return {
success: false,
message: "查询失败",
};
}
}
async function getHisPrescriptionUploadStatus(item) {
const { patientId, orderno, _id } = item;
try {
const url = `${baseUrl}/his/hlw/orderstatus`;
const { status_code, message, data } = await request.main(
url,
{
patientid: patientId,
orderno,
},
"POST"
);
if (status_code == 200 && data && data.orderstatus && data.orderstatus.status == 1) {
return { orderno, hisUploadStatus: 'uploaded', _id }
}
if (status_code == 200 && data && data.orderstatus && data.orderstatus.status == -1) {
return { orderno, hisUploadStatus: 'error', _id }
}
if (status_code == 200 && data && data.orderstatus && data.orderstatus.status == 0) {
return { orderno, hisUploadStatus: 'notUploaded', _id }
}
return {
success: false,
message: message || "查询失败",
};
} catch (err) {
return {
success: false,
message: "查询失败",
};
}
}
async function getHisStoreRegList(item) {
const { storeId: shopid } = item;
if (typeof shopid !== 'string' || shopid.trim() === '') {
return {
success: false,
message: "店铺id无效",
};
}
try {
const url = `${baseUrl}/his/hlw/registerquery`;
const { status_code, message, data } = await request.main(
url,
{
shopid,
requestday: dayjs().format('YYYY-MM-DD HH:mm:ss'),
status: "0",
starttime: dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss'),
endtime: dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss'),
hospid: "108",
},
"POST"
);
if (status_code == 200 && Array.isArray(data)) {
return {
success: true,
list: data,
message: "查询成功",
};
} else {
return {
success: false,
message: message || "查询失败",
};
}
} catch (err) {
return {
success: false,
message: "查询失败",
};
}
}
async function insurancePreSettle(ctx) {
const {
medorg_order_no: orderno,
patientId: patientid,
mdtrtCertNo: mdtrt_cert_no,
mdtrtCertType: mdtrt_cert_type,
cardSn: card_sn,
operator_code,
operator_name,
shopid,
hospid = "108"
} = ctx;
try {
const url = `${baseUrl}/his/hlw/presettle`;
const payload = { orderno, patientid, mdtrt_cert_no, mdtrt_cert_type, card_sn, operator_code, operator_name, hospid, shopid };
const { data, status_code, message } = await request.main(url, payload, "POST");
if (status_code == 200 && !message) {
return { success: true, data, message: '预结算成功' }
}
return { success: false, message: message || '预结算失败' }
} catch (e) {
return { success: false, message: e.message || '预结算失败' }
}
}
async function insuranceSettle(ctx) {
const {
patientid,
orderno,
mdtrt_id,
mdtrt_cert_no,
outparm_1101,
outparm_2206A,
inparm_2207A,
outparm_2204,
totalamount,
selfpay,
operator_code,
operator_name
} = ctx;
const payload = {
patientid,
orderno,
settle_method: '7',
paymethod: '1', // 1: 现金 w: 支付宝 z: 微信
paysn: '',
payrequestsn: '',
mdtrt_id,
mdtrt_cert_no,
outparm_1101,
outparm_2206A,
inparm_2207A,
outparm_2204,
totalamount,
selfpay,
operator_code,
operator_name
}
try {
const url = `${baseUrl}/his/hlw/ybsettle`;
// const url = `${baseUrl}/his/hlw/presettle`;
const { data, status_code, message } = await request.main(url, payload, "POST");
if (status_code == 200 && !message) {
return { success: true, data, message: '结算成功' }
}
return { success: false, message: message || '结算失败' }
} catch (e) {
return { success: false, message: e.message || '结算失败' }
}
}
async function insuranceRefund(ctx) {
const { type, ...payload } = ctx;
try {
const url = `${baseUrl}/his/hlw/ybrefund`;
// const url = `${baseUrl}/his/hlw/presettle`;
const { data, status_code, message } = await request.main(url, payload, "POST");
if (status_code == 200 && !message) {
return { success: true, data, message: '退费成功' }
}
return { success: false, message: message || '退费失败' }
} catch (e) {
return { success: false, message: e.message || '退费失败' }
}
}
async function getRecent90daysDrugRecord(ctx) {
const patientid = typeof ctx.patientId === 'string' ? ctx.patientId.trim() : '';
if (patientid === '') {
return { success: false, message: '患者ID不能为空' }
}
try {
const url = `${baseUrl}/his/hlw/patientzj3100`;
const payload = { patientid, hospid: "108", doctorCode: '10021' };
if (typeof ctx.doctorcode === 'string' && ctx.doctorcode.trim() !== '') {
payload.doctorcode = ctx.doctorcode.trim();
}
const { data, status_code, message } = await request.main(url, payload, "POST");
if (status_code == 200 && !message && data && data.outparm_zj3100) {
// 解析医保用药数据列表
const decodedBuffer = Buffer.from(data.outparm_zj3100, 'base64');
const gbkDecodedStr = decode(decodedBuffer, 'gbk');
const res = JSON.parse(gbkDecodedStr);
const drugList = res && res.data && Array.isArray(res.data.lists) ? res.data.lists.map(i => ({
hiListName: i.hiListName,
insuranceCode: i.hilistCode,
mdtrtId: i.mdtrtId,
quantity: i.cnt,
spec: i.spec && i.spec !== 'null' ? i.spec : '',
unit: i.unit && i.unit !== 'null' ? i.unit : ''
})) : [];
// 解析医保记录
const decodedBuffer1 = Buffer.from(data.outparm_zj3101, 'base64');
const gbkDecodedStr1 = decode(decodedBuffer1, 'gbk');
const res1 = JSON.parse(gbkDecodedStr1);
const recordList = res1 && res1.data && Array.isArray(res1.data.lists) ? res1.data.lists.map(i => ({
mdtrtId: i.mdtrtId,
date: i.admDate,
medinsName: i.medinsName,
})) : [];
return { success: true, drugList, message: '查询成功', recordList }
}
return { success: false, message: message || '查询失败' }
} catch (e) {
return { success: false, message: e.message || '查询失败' }
}
}
async function getHisDrugInfo(item) {
try {
const ignoreAreaCode = typeof item.ignoreAreaCode === 'boolean' ? item.ignoreAreaCode : false;
if (!ignoreAreaCode && (typeof item.areaCode !== 'string' || item.areaCode.trim() === '')) {
return { success: false, message: '区域代码不能为空' }
}
const showAll = typeof item.showAll === 'boolean' ? item.showAll : false;
const gjbm = Array.isArray(item.insuranceCodes) ? item.insuranceCodes.filter(i => typeof i === 'string' && i.trim() !== '') : [];
console.log('gjbm: ', gjbm)
if (gjbm.length === 0 && !showAll) {
return { success: false, message: '医保编码不能为空' }
}
const url = `${baseUrl}/his/hlw/drug`;
const payload = {
hospid: '108',
drug_type: 'xy',
area_code: typeof item.areaCode === 'string' ? item.areaCode.trim() : '',
gjbm: gjbm.join(',')
}
const { code, status_code, message, data } = await request.main(
url,
payload,
"POST"
);
if (status_code == 200 && code == '1' && !message) {
return {
success: true,
message: "[his]查询药品信息成功",
data: data && Array.isArray(data.drugs) ? data.drugs : []
};
} else {
return {
success: false,
message: `[his]查询药品信息传失败: ${message || '未知错误'}`,
};
}
} catch (err) {
return {
success: false,
message: `[his]查询药品信息传错误: ${err.message || '未知'}`,
};
}
}
async function uploadMedicinePurchaseRx(item) {
try {
const { data } = item;
const url = `${baseUrl}/his/hlw/mzordersave`;
const { code, status_code, message } = await request.main(
url,
data,
"POST"
);
if (status_code == 200 && code == '1' && !message) {
return {
success: true,
message: "[his]购药订单处上传成功",
};
} else {
return {
success: false,
message: `[his]购药订单处方上传失败: ${message || '未知错误'}`,
};
}
} catch (err) {
return {
success: false,
message: `[his]购药订单处方上传错误: ${err.message || '未知'}`,
};
}
}
async function uploadWinePurchaseRx(item) {
try {
const { data } = item;
const url = `${baseUrl}/his/hlw/tcsave`;
const { code, status_code, message } = await request.main(
url,
data,
"POST"
);
console.log('data', code, status_code, message);
if (status_code == 200 && code == '1' && !message) {
return {
success: true,
message: "[his]浸酒处上传成功",
};
} else {
return {
success: false,
message: `[his]浸酒处方上传失败: ${message || '未知错误'}`,
};
}
} catch (err) {
return {
success: false,
message: `[his]浸酒处方上传错误: ${err.message || '未知'}`,
};
}
}
// 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)
return {
success: true,
advices,
message: "[his]医保审核成功",
};
} else {
return {
success: false,
message: `[his]医保审核失败: ${message || '未知错误'}`,
};
}
} catch (err) {
return {
success: false,
message: `[his]医保审核错误: ${err.message || '未知错误'}`,
};
}
}
async function getHisMedicinePurchase({ patientId, orderNo }) {
try {
const url = `${baseUrl}/his/hlw/orderquery`;
const { code, status_code, message, data } = await request.main(url, { patientid: patientId, orderno: orderNo }, "POST");
const order = data && Array.isArray(data.orders) ? data.orders[0] : null;
if (status_code == 200 && code == '1' && !message) {
if (order) {
return { success: true, data: order, message: '查询成功' }
}
return { success: false, message: `[his]处方订单查询失败: 未找到订单` }
}
return {
success: false,
message: `[his]处方订单查询失败: ${message || '未知错误'}`,
};
} catch (err) {
return { success: false, message: `[his]处方订单查询错误: ${err.message || '未知错误'}` };
}
}
async function cancelMedicinePurchase({ patientId: patientid, orderNo: orderno }) {
try {
const url = `${baseUrl}/his/hlw/ordercancel`;
const { code, status_code, message } = await request.main(url, { patientid, orderno }, "POST");
if (status_code == 200 && code == '1' && !message) {
return { success: true, message: '[his]取消购药订单处方成功' }
}
return { success: false, message: `[his]取消购药订单处方失败: ${message || '未知错误'}` }
} catch (err) {
return { success: false, message: `[his]取消购药订单处方错误: ${err.message || '未知错误'}` };
}
}
async function refundMedicinePurchase({ patientId: patientid, orderNo: orderno }) {
try {
const url = `${baseUrl}/his/hlw/orderrefund`;
const { code, status_code, message } = await request.main(url, { patientid, orderno }, "POST");
if (status_code == 200 && code == '1' && !message) {
return { success: true, message: '[his]退费购药订单处方成功' }
}
return { success: false, message: `[his]退费购药订单处方失败: ${message || '未知错误'}` }
} catch (err) {
return { success: false, message: `[his]退费购药订单处方错误: ${err.message || '未知错误'}` };
}
}
async function getHisInvoice(item) {
try {
const { patientId, orderNo } = item;
const url = `${baseUrl}/his/hlw/dzfp`;
let { code, status_code, message, data } = await request.main(url, { patientid: patientId, orderno: orderNo }, "POST");
if (status_code == 200 && code == '1' && !message) {
const dzfp = data && Array.isArray(data.dzfp) ? data.dzfp[0] : null;
return { success: true, data: dzfp && dzfp.dzfp_url ? dzfp.dzfp_url : '', message: '发票查询成功' }
}
return { success: false, message: `[his]发票查询失败: ${message || '未知错误'}` }
} catch (err) {
return { success: false, message: `[his]发票查询错误: ${err.message || '未知错误'}` };
}
}
async function updateMedicinePurchaseDelivery(item) {
try {
const {
patientId: patientid,
orderNo: orderno,
backFlag: back_flag = 0,
shippingType: deliv_type,
shippingNo: deliv_no,
shippingFee: deliv_fee,
pickupCode: tracking_no,
payTime: pay_dt,
tradeNo: pay_orderno,
receiveName: shipp_name,
receivePhone: shipp_tel,
fullAddress: shipp_addr,
invoice
} = item;
const payload = {
patientid,
orderno,
back_flag, // 0 收取运费 | 1 退还运费
deliv_type,
deliv_no,
deliv_fee,
pay_dt,
pay_orderno,
shipp_name,
shipp_tel,
shipp_addr,
tracking_no,
invoice
}
const url = `${baseUrl}/his/hlw/delivery`;
let { code, status_code, message, data } = await request.main(url, payload, "POST");
if (status_code == 200 && code == '1' && !message) {
return { success: true, data, message: '[his]同步配药订单物流信息成功' }
}
return { success: false, message: `[his]同步配药订单物流信息失败: ${message || '未知错误'}` }
} catch (err) {
return { success: false, message: `[his]同步配药订单物流信息错误: ${err.message || '未知错误'}` };
}
}
async function syncHisChargeStatus(ctx) {
try {
const payload = {
patientid: ctx.patientId,
orderno: ctx.orderNo,
settle_method: 1,
paymethod: 'h',
paysn: ctx.tradeNo,
payrequestsn: ctx.outTradeNo,
totalamount: ctx.totalAmount,
selfpay: ctx.totalAmount,
operator_code: 'hlw',
operator_name: '互联网医院'
}
const url = `${baseUrl}/his/hlw/ybsettle`;
let { code, status_code, message, data } = await request.main(url, payload, "POST");
if (status_code == 200 && code == '1' && !message) {
return { success: true, data, message: '[his]同步浸酒订单收费成功' }
}
return { success: false, message: `[his]同步浸酒订单收费失败: ${message || '未知错误'}` }
} catch (err) {
return { success: false, message: `[his]同步浸酒订单收费错误: ${err.message || '未知错误'}` };
}
}
async function syncHisRefundStatus(ctx) {
try {
const payload = {
patientid: ctx.patientId,
orderno: ctx.orderNo,
ptlsh: ctx.refundSerialNo
}
const url = `${baseUrl}/his/hlw/orderrefund`;
let { code, status_code, message, data } = await request.main(url, payload, "POST");
if (status_code == 200 && code == '1' && !message) {
return { success: true, data, message: '[his]同步浸酒订单退费成功' }
}
return { success: false, message: `[his]同步浸酒订单退费失败: ${message || '未知错误'}` }
} catch (err) {
return { success: false, message: `[his]同步浸酒订单退费错误: ${err.message || '未知错误'}` };
}
}
async function getHisEmployeeInfo(item) {
const { code } = item;
try {
const testUrl = "http://115.239.161.48:8089";
const url = `${testUrl}/his/hlw/employeeinfo`;
const { data, status_code, message } = await request.main(
url,
{ type: "01", code: code },
"POST"
);
if (status_code == 200 && !message) {
return {
success: true,
data,
message: "获取成功",
}
}
} catch (e) {
return { success: false, message: e?.message }
}
}
async function getWineDrugStockInfo(item) {
try {
if (typeof item.code !== 'string' || item.code.trim() === '') {
return { success: false, message: '中药饮片代码不能为空' }
}
if (!(Number.isInteger(item.num) && item.num > 0)) {
return { success: false, message: '中药饮片数量必须是整数且大于0' }
}
const url = `${baseUrl}/his/hlw/drug`;
const payload = {
hospid: '108',
drug_type: 'zcy',
tc_code: item.code.trim(),
tc_number: item.num
}
const { code, status_code, message, data } = await request.main(
url,
payload,
"POST"
);
if (status_code == 200 && code == '1') {
return {
success: true,
message: "[his]查询药品信息成功",
data: data
};
}
return {
success: false,
message: `中药饮品查询失败: ${message || '未知错误'}`,
};
} catch (err) {
return {
success: false,
message: `中药饮品查询失败错误: ${err.message || '未知'}`,
};
}
}
async function hisTest() {
// const data = {
// patientId: '10428622',
// orderNo: 'R260530160013084765664',
// tradeNo: '3JHR2605301770353161405592',
// outTradeNo: '3JHR260530160352836640789',
// totalAmount: 78
// }
// 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);
}

220
hlw/hn-his/notify.js Normal file
View File

@ -0,0 +1,220 @@
const { getDatabase } = require('../../mongodb')
const Sm4Util = require('../../utils/sm4-util')
const CodeMap = {
updateOnlineDrug: '01'// 同步his在线药品信息
}
let hlwDb = null;
function isValidString(str) {
return typeof str === 'string' && str.trim() !== '' && !(/^null$/i.test(str.trim())) && str.length < 200;
}
async function getHlwDb() {
try {
if (hlwDb) return hlwDb;
hlwDb = await getDatabase('Internet-hospital');
if (!hlwDb) throw new Error('数据库连接失败');
return hlwDb;
} catch (e) {
return Promise.reject(e)
}
}
async function handleNotify(ctx) {
let data = null;
try {
const ciphertext = ctx && typeof ctx.data === 'string' ? ctx.data.trim() : '';
const jsonStr = Sm4Util.decryptDataForSm3(ciphertext);
data = JSON.parse(jsonStr);
if (Object.prototype.toString.call(data) !== '[object Object]') {
return { success: false, message: '无效的通知类型' }
}
if (data.code === CodeMap.updateOnlineDrug) {
return await updateOnlineDrug(data)
}
return { success: false, message: '无效的通知类型' }
} catch (e) {
return { success: false, message: `参数错误: ${e.message}` }
}
}
async function updateOnlineDrug(data) {
try {
const db = await getHlwDb();
const corpId = typeof data.corpId === 'string' ? data.corpId.trim() : '';
const erp_id = typeof data.erp_id === 'string' ? data.erp_id.trim() : '';
const drugname = typeof data.drugname === 'string' ? data.drugname.trim() : '';
const specification = typeof data.specification === 'string' ? data.specification.trim() : '';
const pack_unit_name = typeof data.pack_unit_name === 'string' ? data.pack_unit_name.trim() : '';
const manu_name = typeof data.manu_name === 'string' ? data.manu_name.trim() : '';
const gjbm = typeof data.gjbm === 'string' ? data.gjbm.trim() : '';
const py_code = typeof data.py_code === 'string' ? data.py_code.trim() : '';
const default_dosage = Number(data.default_dosage);
const default_unit = typeof data.default_unit === 'string' ? data.default_unit.trim() : '';
const default_drug_win = Number(data.default_drug_win);
const default_total = Number(data.default_total);
const default_usage_name = typeof data.default_usage_name === 'string' ? data.default_usage_name.trim() : '';
const default_supply_name = typeof data.default_supply_name === 'string' ? data.default_supply_name.trim() : '';
const newData = {};
const effectKeys = [];
if (!corpId) {
return { success: false, message: '参数错误' }
}
if (isValidString(erp_id) && Number(erp_id) > 0 && Number.isInteger(Number(erp_id))) {
newData.product_id = Number(erp_id);
newData.product_id_str = Number(erp_id).toString();
} else {
return { success: false, message: 'erp id格式错误(正整数)' }
}
if (isValidString(drugname)) {
newData.name = drugname;
effectKeys.push('drugname')
} else {
return { success: false, message: '药瓶名称不能为空' }
}
if (isValidString(gjbm)) {
newData.insurance_code = gjbm;
effectKeys.push('gjbm')
} else {
return { success: false, message: '医保编码不能为空' }
}
if (isValidString(specification)) {
newData.specification = specification;
effectKeys.push('specification')
}
if (isValidString(manu_name)) {
newData.manufacturer = manu_name;
effectKeys.push('manu_name')
}
if (isValidString(py_code)) {
newData.pinyin_code = py_code;
effectKeys.push('py_code')
}
if (default_dosage > 0 && (default_dosage * 100) % 1 === 0) {
newData.dosage = default_dosage
effectKeys.push('default_dosage')
}
if (default_drug_win > 0 && Number.isInteger(default_drug_win)) {
newData.days = default_drug_win
effectKeys.push('default_drug_win')
}
if (default_total > 0 && Number.isInteger(default_total)) {
newData.recommended_quantity = default_total
effectKeys.push('default_total')
}
const configKeys = [
`${corpId}-medicine-package-unit`,// 药品包装单位
`${corpId}-online-medicine-dosage-unit`,//线上药品单次剂量单位
`${corpId}-online-medicine-frequence`,//线上药品用药频率
`${corpId}-online-medicine-administration`,//线上药品用药用法
]
const res = await db.collection('hlw-config').find({ corpId: { $in: configKeys } }, { projection: { corpId: 1, list: 1 } }).toArray()
if (isValidString(pack_unit_name)) {
const packUnitConfig = res.find(i => i.corpId === `${corpId}-medicine-package-unit`)
const list = packUnitConfig && Array.isArray(packUnitConfig.list) ? packUnitConfig.list : [];
const packUnit = list.find(i => i.name === pack_unit_name);
if (packUnit) {
newData.unit = packUnit.name
effectKeys.push('pack_unit_name')
}
}
if (isValidString(default_unit)) {
const dosageUnitConfig = res.find(i => i.corpId === `${corpId}-online-medicine-dosage-unit`)
const list = dosageUnitConfig && Array.isArray(dosageUnitConfig.list) ? dosageUnitConfig.list : [];
const dosageUnit = list.find(i => i.name === default_unit);
if (dosageUnit) {
newData.dosage_unit = dosageUnit.name;
effectKeys.push('default_unit')
}
}
if (isValidString(default_usage_name)) {
const administrationConfig = res.find(i => i.corpId === `${corpId}-online-medicine-administration`)
const list = administrationConfig && Array.isArray(administrationConfig.list) ? administrationConfig.list : [];
const usage = list.find(i => i.name === default_usage_name);
if (usage) {
newData.administration_method = usage.name;
effectKeys.push('default_usage_name')
}
}
if (isValidString(default_supply_name)) {
const supplyConfig = res.find(i => i.corpId === `${corpId}-online-medicine-frequence`)
const list = supplyConfig && Array.isArray(supplyConfig.list) ? supplyConfig.list : [];
const supply = list.find(i => i.name === default_supply_name);
if (supply) {
newData.frequency = supply.name;
newData.freq = supply.name;
effectKeys.push('default_supply_name')
}
}
// return { success: true, data, effectKeys }
const med = await db.collection('online-drug-info').findOne({ product_id: Number(erp_id) });
if (med) {
newData.updateTime = Date.now();
newData.updater = 'hisPush';
await db.collection('online-drug-info').updateOne({ _id: med._id }, { $set: newData })
return { success: true, message: '更新成功', valid_keys: effectKeys.join() }
} else {
newData.createTime = Date.now();
newData.creator = 'hisPush';
newData.onSale = false;
await db.collection('online-drug-info').insertOne(newData)
return { success: true, message: '新增成功', valid_keys: effectKeys.join() }
}
} catch (e) {
return { success: false, message: `更新失败: ${e.message}` }
}
}
async function notifyTest() {
const str = getPayload()
const res = await handleNotify(str);
return res
}
function getPayload() {
// const data = {
// code: '01',
// corpId: '4G7H8J2K9L0M1N3P5Q6R7S8T9U0V1',
// erp_id: '0001',
// drugname: '药品名称1',
// specification: '药品规格1',
// pack_unit_name: '瓶',
// manu_name: '厂商名称1',
// gjbm: '医保编码1',
// py_code: 'pinyin1',
// default_dosage: 2,
// default_unit: '颗',
// default_usage_name: '口服',
// default_drug_win: 8,
// default_supply_name: '每天二次',
// default_total: 4
// };
const data = {
"code": "01",
"corpId": "4G7H8J2K9L0M1N3P5Q6R7S8T9U0V1",
"erp_id": "52362",
"drugname": "(21金维他)多维元素片(21)",
"specification": "60粒×5瓶/条 ",
"pack_unit_name": "条",
"manu_name": "杭州民生健康药业有限公司",
"gjbm": "XA11AAD190A001020904494",
"py_code": "(21JWT)D",
"default_dosage": null,
"default_unit": null,
"default_usage_name": null,
"default_drug_win": null,
"default_supply_name": null,
"default_total": null
}
return { data: Sm4Util.encryptDataForSm3(JSON.stringify(data)) }
}
module.exports = {
handleNotify,
notifyTest
}

54
hlw/hn-his/types.ts Normal file
View File

@ -0,0 +1,54 @@
// interface UpdateOnlineMedicine {
// erp_id: string;
// drugname: string; // 药品名称
// specification: string; // 规格
// pack_unit_name: string;// 包装单位
// manu_name: string; // 厂家
// gjbm: string; // 医保码
// py_code: string; // 拼音码
// default_dosage: number; // 单次剂量 (最多2位小数)
// default_unit: string; // 单次剂量单位
// default_usage_name: string; // 用法
// default_drug_win: number;// 用药天数
// default_supply_name: string; // 用药频率
// default_total: number; // 推荐数量
// }
interface UpdateOnlineMedicineBody {
code: '01';
corpId: string;
erp_id: string;
drugname: string; // 药品名称
specification: string; // 规格
pack_unit_name: string;// 包装单位
manu_name: string; // 厂家
gjbm: string; // 医保码
py_code: string; // 拼音码
default_dosage: number; // 单次剂量 (最多2位小数)
default_unit: string; // 单次剂量单位
default_usage_name: string; // 用法
default_drug_win: number;// 用药天数
default_supply_name: string; // 用药频率
default_total: number; // 推荐数量
}
const body: UpdateOnlineMedicineBody = {
code: '01',
corpId: '4G7H8J2K9L0M1N3P5Q6R7S8T9U0V1',
erp_id: '0001',
drugname: '123',
specification: '123',
pack_unit_name: '盒',
manu_name: '123',
gjbm: '123',
py_code: '1222',
default_dosage: 1,
default_unit: '片',
default_usage_name: '口服',
default_drug_win: 7,
default_supply_name: '每天一次',
default_total: 3
}

View File

@ -1,4 +1,4 @@
const zytHis = require("./zyt-his"); const hnHis = require("./hn-his");
const drugInfo = require("./drug-info"); const drugInfo = require("./drug-info");
const patientDescription = require("./patient-description"); const patientDescription = require("./patient-description");
const tencentIM = require("./tencent-im"); const tencentIM = require("./tencent-im");
@ -27,8 +27,9 @@ const miniAppLog = require("./mini-app-log");
const chineseMedicine = require("./chinese-medicine"); const chineseMedicine = require("./chinese-medicine");
const chineseMedicineOrder = require("./chinese-medicine-order"); const chineseMedicineOrder = require("./chinese-medicine-order");
const hlwPatient = require("./hlw-patient"); const hlwPatient = require("./hlw-patient");
const scannedCardArchive = require("./scanned-card-archive");
module.exports = async (item, db, res) => { module.exports = async (item, db, context) => {
switch (item.type) { switch (item.type) {
case "getHisCustomer": case "getHisCustomer":
case "addHisCustomer": case "addHisCustomer":
@ -42,7 +43,7 @@ module.exports = async (item, db, res) => {
case "getHisInvoice": case "getHisInvoice":
case "getHisEmployeeInfo": case "getHisEmployeeInfo":
case "getWineDrugStockInfo": case "getWineDrugStockInfo":
return await zytHis(item, db); return await hnHis(item, db);
case "getDrugInfo": case "getDrugInfo":
case "deleteDrugInfo": case "deleteDrugInfo":
case "updateDrugInfo": case "updateDrugInfo":
@ -129,6 +130,7 @@ module.exports = async (item, db, res) => {
case "getLatestSubmitTime": case "getLatestSubmitTime":
case "orderHasDiagnosis": case "orderHasDiagnosis":
case "getStoreDiagnosisRecord": case "getStoreDiagnosisRecord":
case "markStoreRxPrinted":
case "getAllDiagnosisRecord": case "getAllDiagnosisRecord":
case "getDoctorRxStats": case "getDoctorRxStats":
case "getPharmacistRxStats": case "getPharmacistRxStats":
@ -145,7 +147,7 @@ module.exports = async (item, db, res) => {
case "getDiagnosisById": case "getDiagnosisById":
case "getPatientWineBuyCount": case "getPatientWineBuyCount":
case "getYbReviewResult": case "getYbReviewResult":
return diagnosticRecord(item, db); return diagnosticRecord(item, db, context);
case "caAuth": case "caAuth":
case "getSealImage": case "getSealImage":
case "getCaUserInfo": case "getCaUserInfo":
@ -183,7 +185,7 @@ module.exports = async (item, db, res) => {
case "getRegPayStatus": case "getRegPayStatus":
return await hlwReg(item, db); return await hlwReg(item, db);
case "downloadXlsx": case "downloadXlsx":
return await downloadXlsx(item, db, res); return await downloadXlsx(item, db, context);
case "getDoctorTimeStats": case "getDoctorTimeStats":
return await hlwTimeDuration(item, db); return await hlwTimeDuration(item, db);
case "getHlwOrderConfig": case "getHlwOrderConfig":
@ -267,6 +269,8 @@ module.exports = async (item, db, res) => {
case "updateHlwPatient": case "updateHlwPatient":
case "deleteHlwPatient": case "deleteHlwPatient":
return await hlwPatient.main(item, db); return await hlwPatient.main(item, db);
case "saveScannedCard":
return await scannedCardArchive(item, db);
default: default:
return { return {
success: false, success: false,

View File

@ -26,7 +26,7 @@ async function getBatchInsertOnlineDrugFn() {
rowData[key.trim()] = row[key]; rowData[key.trim()] = row[key];
} }
if (!rowData.his_drug_code || !String(rowData.his_drug_code).trim()) return; if (!rowData.his_drug_code || !String(rowData.his_drug_code).trim()) return;
const [drugData] = getDrugList([rowData], true, extraInfo); const [drugData] = getDrugList([rowData], true, extraInfo, 'online');
const { msgs, ...drug } = drugData; const { msgs, ...drug } = drugData;
if (msgs.length === 0) { if (msgs.length === 0) {
batch.push(drug); batch.push(drug);

View File

@ -181,7 +181,7 @@ async function addOnlineDrugInfo(ctx, db) {
return { success: false, message: "ERP编码已存在" }; return { success: false, message: "ERP编码已存在" };
} }
} }
const [item] = getDrugList([data], true, peopleInfo); const [item] = getDrugList([data], true, peopleInfo, 'online');
const { msgs, ...drug } = item; const { msgs, ...drug } = item;
if (msgs.length) { if (msgs.length) {
return { success: false, message: msgs.join(",") }; return { success: false, message: msgs.join(",") };
@ -236,7 +236,7 @@ async function editOnlineDrugInfo(ctx, db) {
typeof ctx.operatorId === "string" ? ctx.operatorId : "", typeof ctx.operatorId === "string" ? ctx.operatorId : "",
updateTime: Date.now(), updateTime: Date.now(),
}; };
const [item] = getDrugList([data], false, peopleInfo); const [item] = getDrugList([data], false, peopleInfo, 'online');
const { msgs, ...drug } = item; const { msgs, ...drug } = item;
if (msgs.length) { if (msgs.length) {
return { success: false, message: msgs.join(",") }; return { success: false, message: msgs.join(",") };
@ -321,7 +321,7 @@ async function importOnlineDrugInfo(ctx, db) {
typeof ctx.operatorId === "string" ? ctx.operatorId : "", typeof ctx.operatorId === "string" ? ctx.operatorId : "",
createTime: Date.now(), createTime: Date.now(),
}; };
const datalist = getDrugList(drugs, true, peopleInfo); const datalist = getDrugList(drugs, true, peopleInfo, 'online');
datalist.forEach((i) => { datalist.forEach((i) => {
if (!i.his_drug_code || !String(i.his_drug_code).trim()) { if (!i.his_drug_code || !String(i.his_drug_code).trim()) {
i.msgs.push("ERP编码不能为空"); i.msgs.push("ERP编码不能为空");

View File

@ -0,0 +1,87 @@
const Sm4Util = require("../../utils/sm4-util");
const CARD_STRING_FIELDS = [
"CardIDCode",
"CardNum",
"CardType",
"Gender",
"IDNum",
"Name",
"PsamTN",
"Message",
"flag",
"ylbxtcqbm",
];
function normalizeCardData(value) {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return null;
}
const data = {
CanOpen: value.CanOpen === true,
Success: value.Success === true,
};
for (const field of CARD_STRING_FIELDS) {
if (value[field] === undefined || value[field] === null) continue;
if (typeof value[field] !== "string") return null;
const text = value[field].trim();
if (text.length > 256) return null;
data[field] = text;
}
if (!data.CanOpen || !data.Success) return null;
if (!data.IDNum && !data.CardNum && !data.CardIDCode) return null;
return data;
}
function normalizeString(value, maxLength) {
if (typeof value !== "string") return "";
const text = value.trim();
return text.length <= maxLength ? text : "";
}
async function saveScannedCard(item, db) {
const cardData = normalizeCardData(item.cardData);
if (!cardData) {
return { success: false, message: "卡片数据无效" };
}
try {
const encryptedCardData = Sm4Util.encryptDataForSm3(JSON.stringify(cardData));
const storeIds = Array.isArray(item.storeIds)
? [...new Set(item.storeIds.map((value) => normalizeString(value, 100)).filter(Boolean))]
: [];
const record = {
encryptedCardData,
encryption: "SM4",
dataVersion: 1,
readerType: normalizeString(item.readerType, 50) || "ZJ_HZ_310000",
corpId: normalizeString(item.corpId, 100),
storeIds,
createTime: Date.now(),
};
const result = await db.collection("scanned-card-archive").insertOne(record);
return {
success: true,
id: result.insertedId.toString(),
message: "卡片数据保存成功",
};
} catch (error) {
console.error("保存扫码建档数据失败:", error.message);
return { success: false, message: "保存卡片数据失败" };
}
}
module.exports = async (item, db) => {
switch (item.type) {
case "saveScannedCard":
return saveScannedCard(item, db);
default:
return { success: false, message: "未找到接口" };
}
};
module.exports.normalizeCardData = normalizeCardData;
module.exports.saveScannedCard = saveScannedCard;

View File

@ -0,0 +1,74 @@
jest.mock("../../utils/sm4-util", () => ({
encryptDataForSm3: jest.fn(() => "encrypted-card-data"),
}));
const Sm4Util = require("../../utils/sm4-util");
const scannedCardArchive = require("./index");
describe("scanned-card-archive", () => {
test("encrypts the full normalized card payload and only stores ciphertext", async () => {
const insertOne = jest.fn().mockResolvedValue({
insertedId: { toString: () => "archive-id" },
});
const db = {
collection: jest.fn(() => ({ insertOne })),
};
const cardData = {
CanOpen: true,
CardIDCode: "3306012345678901A",
CardNum: "DB5712345",
CardType: "3",
Gender: "",
IDNum: "330601199001011234",
Name: "张三",
PsamTN: "330412345678",
Success: true,
flag: "1",
ylbxtcqbm: "330600",
ignoredField: "must-not-be-saved",
};
const result = await scannedCardArchive({
type: "saveScannedCard",
cardData,
readerType: "ZJ_HZ_310000",
corpId: "corp-1",
storeIds: ["store-1"],
}, db);
expect(result).toEqual({
success: true,
id: "archive-id",
message: "卡片数据保存成功",
});
expect(Sm4Util.encryptDataForSm3).toHaveBeenCalledTimes(1);
const encryptedInput = JSON.parse(Sm4Util.encryptDataForSm3.mock.calls[0][0]);
expect(encryptedInput).toEqual(expect.objectContaining({
IDNum: cardData.IDNum,
Name: cardData.Name,
Success: true,
}));
expect(encryptedInput).not.toHaveProperty("ignoredField");
const stored = insertOne.mock.calls[0][0];
expect(stored.encryptedCardData).toBe("encrypted-card-data");
expect(stored).not.toHaveProperty("IDNum");
expect(stored).not.toHaveProperty("Name");
expect(stored).not.toHaveProperty("cardData");
});
test("rejects unsuccessful or malformed card payloads", async () => {
const db = { collection: jest.fn() };
const result = await scannedCardArchive({
type: "saveScannedCard",
cardData: {
CanOpen: false,
Success: false,
Message: "-2无卡",
},
}, db);
expect(result).toEqual({ success: false, message: "卡片数据无效" });
expect(db.collection).not.toHaveBeenCalled();
});
});

View File

@ -14,6 +14,8 @@ module.exports = async (item, mongodb) => {
return await setUserProfile(item); return await setUserProfile(item);
case "generateUserSig": case "generateUserSig":
return generateUserSig(item); return generateUserSig(item);
case "importAccount":
return await importAccount(item);
case "deleteSession": case "deleteSession":
return deleteSession(item); return deleteSession(item);
case "deleteFriend": case "deleteFriend":
@ -39,28 +41,35 @@ async function sendSystemNotification(item) {
const { formAccount, toAccount, msgBody, SyncOtherMachine, corpId } = item; const { formAccount, toAccount, msgBody, SyncOtherMachine, corpId } = item;
console.log("发送系统通知请求体msgBody:", msgBody); console.log("发送系统通知请求体msgBody:", msgBody);
console.log("item", item); console.log("item", item);
const { sdkAppId, identifier, secretKey } = await getConfig(corpId);
const { data: userSig } = await generateUserSig({
userId: identifier,
corpId,
sdkAppId,
secretKey,
});
// 构造请求 URL
// 构造请求体
const data = {
SyncOtherMachine, // 若不希望将消息同步至 From_Account,则 SyncOtherMachine 填写2;若希望将消息同步至 From_Account,则 SyncOtherMachine 填写1。
From_Account: formAccount, // 发送方
To_Account: toAccount, // 接收方
MsgRandom: Math.floor(Math.random() * 1000000), // 消息随机数(32位无符号整数),后台用于同一秒内的消息去重。请确保该字段填的是随机数
MsgBody: msgBody,
SupportMessageExtension: 1,
};
console.log("发送系统通知请求体:", data);
const url = `https://console.tim.qq.com/v4/openim/sendmsg?sdkappid=${sdkAppId}&identifier=${identifier}&usersig=${userSig}&random=${random}&contenttype=json`;
try { try {
const { sdkAppId, identifier, secretKey } = (await getConfig(corpId)) || {};
const sigResult = await generateUserSig({
userId: identifier,
corpId,
sdkAppId,
secretKey,
});
if (!sigResult.success) {
return sigResult;
}
// 构造请求 URL
// 构造请求体
const data = {
SyncOtherMachine, // 若不希望将消息同步至 From_Account,则 SyncOtherMachine 填写2;若希望将消息同步至 From_Account,则 SyncOtherMachine 填写1。
From_Account: formAccount, // 发送方
To_Account: toAccount, // 接收方
MsgRandom: Math.floor(Math.random() * 1000000), // 消息随机数(32位无符号整数),后台用于同一秒内的消息去重。请确保该字段填的是随机数
MsgBody: msgBody,
SupportMessageExtension: 1,
};
console.log("发送系统通知请求体:", data);
const url = `https://console.tim.qq.com/v4/openim/sendmsg?sdkappid=${sdkAppId}&identifier=${identifier}&usersig=${sigResult.data}&random=${random}&contenttype=json`;
const response = await request.main(url, data, "POST"); const response = await request.main(url, data, "POST");
console.log("系统通知发送成功:", response.data); if (!isTencentIMSuccess(response)) {
console.error("发送系统通知失败:", response);
return createTencentIMFailure("系统通知发送失败", response);
}
console.log("系统通知发送成功:", response);
return { return {
success: true, success: true,
message: "系统通知发送成功", message: "系统通知发送成功",
@ -77,6 +86,24 @@ async function sendSystemNotification(item) {
}; };
} }
} }
function isTencentIMSuccess(response) {
return Boolean(
response &&
response.ActionStatus === "OK" &&
Number(response.ErrorCode) === 0
);
}
function createTencentIMFailure(message, response) {
return {
success: false,
message,
errorCode: response && response.ErrorCode,
errorInfo: response && response.ErrorInfo,
error: response,
};
}
/** /**
* *
* 用户状态 Tag_Profile_Custom_Status * 用户状态 Tag_Profile_Custom_Status
@ -330,67 +357,77 @@ async function deleteFriend(item) {
} }
} }
// 批量导入医生账号 // 将业务账号导入腾讯 IM。重复导入同一账号是幂等操作。
async function importDoctorAccount(event) { async function importAccount(item) {
console.log("用户资料设置求体:", item); const { account, corpId } = item;
const { formAccount, profileItem, corpId } = item; if (typeof account !== "string" || !account.trim()) {
const {
sdkAppId,
identifier,
secretKey,
} = await getConfig(corpId);
if (!sdkAppId) {
return { return {
success: false, success: false,
message: "未找到企业配置", message: "IM 账号不能为空",
}; };
} }
const { data: userSig } = await generateUserSig({
userId: identifier,
corpId,
sdkAppId,
secretKey,
});
// 构造请求 URL
// 构造请求体
const data = {
AccountList: [],
};
const url = `https://console.tim.qq.com/v4/im_open_login_svc/multiaccount_import?sdkappid=${sdkAppId}&identifier=${identifier}&usersig=${userSig}&random=${random}&contenttype=json`;
try { try {
const { sdkAppId, identifier, secretKey } = (await getConfig(corpId)) || {};
const sigResult = await generateUserSig({
userId: identifier,
corpId,
sdkAppId,
secretKey,
});
if (!sigResult.success) {
return sigResult;
}
const data = {
Identifier: account.trim(),
};
const url = `https://console.tim.qq.com/v4/im_open_login_svc/account_import?sdkappid=${sdkAppId}&identifier=${identifier}&usersig=${sigResult.data}&random=${random}&contenttype=json`;
const response = await request.main(url, data, "POST"); const response = await request.main(url, data, "POST");
console.log("用户资料设置", response.data); if (!isTencentIMSuccess(response)) {
console.error("腾讯 IM 账号导入失败:", response);
return createTencentIMFailure("腾讯 IM 账号导入失败", response);
}
console.log("腾讯 IM 账号导入成功:", account);
return { return {
success: true, success: true,
message: "用户资料设置成功", message: "腾讯 IM 账号导入成功",
}; };
} catch (error) { } catch (error) {
return { return {
success: false, success: false,
message: "用户资料设置失败", message: "腾讯 IM 账号导入失败",
error: error.response ? error.response.data : error.message, error: error.response ? error.response.data : error.message,
}; };
} }
} }
async function generateUserSig({ userId, corpId, sdkAppId, secretKey }) { async function generateUserSig({ userId, corpId, sdkAppId, secretKey }) {
if (!sdkAppId || !secretKey) { try {
const res = await getConfig(corpId); if (!sdkAppId || !secretKey) {
sdkAppId = res.sdkAppId; const res = (await getConfig(corpId)) || {};
secretKey = res.secretKey; sdkAppId = res.sdkAppId;
} secretKey = res.secretKey;
if (!sdkAppId) { }
if (!sdkAppId || !secretKey || !userId) {
return {
success: false,
message: "未找到完整的企业 IM 配置",
};
}
var api = new TLSSigAPIv2.Api(sdkAppId, secretKey);
var sig = api.genSig(userId, 86400 * 180);
return {
success: true,
message: "生成成功",
data: sig,
sdkAppId,
};
} catch (error) {
return { return {
success: false, success: false,
message: "未找到企业配置", message: "生成 UserSig 失败",
error: error.message,
}; };
} }
var api = new TLSSigAPIv2.Api(sdkAppId, secretKey);
var sig = api.genSig(userId, 86400 * 180);
return {
success: true,
message: "生成成功",
data: sig,
sdkAppId,
};
} }

View File

@ -0,0 +1,178 @@
jest.mock("../../request", () => ({
main: jest.fn(),
}));
jest.mock("../utils/config", () => ({
getConfig: jest.fn(),
}));
jest.mock("./TLSSigAPIv2", () => ({
Api: jest.fn(),
}));
const request = require("../../request");
const { getConfig } = require("../utils/config");
const TLSSigAPIv2 = require("./TLSSigAPIv2");
const tencentIM = require("./index");
describe("tencent-im", () => {
beforeEach(() => {
jest.clearAllMocks();
jest.spyOn(console, "log").mockImplementation(() => {});
jest.spyOn(console, "error").mockImplementation(() => {});
getConfig.mockResolvedValue({
sdkAppId: 1600000000,
identifier: "administrator",
secretKey: "test-secret",
});
TLSSigAPIv2.Api.mockImplementation(() => ({
genSig: jest.fn(() => "test-user-sig"),
}));
});
afterEach(() => {
jest.restoreAllMocks();
});
test("imports a new account", async () => {
request.main.mockResolvedValue({
ActionStatus: "OK",
ErrorCode: 0,
ErrorInfo: "",
});
const result = await tencentIM({
type: "importAccount",
account: "WZTEST001",
corpId: "corp-1",
});
expect(result).toEqual({
success: true,
message: "腾讯 IM 账号导入成功",
});
expect(request.main).toHaveBeenCalledWith(
expect.stringContaining("/v4/im_open_login_svc/account_import?"),
{ Identifier: "WZTEST001" },
"POST"
);
});
test("treats repeated successful imports as success", async () => {
request.main.mockResolvedValue({
ActionStatus: "OK",
ErrorCode: 0,
ErrorInfo: "",
});
const first = await tencentIM({
type: "importAccount",
account: "WZTEST001",
corpId: "corp-1",
});
const second = await tencentIM({
type: "importAccount",
account: "WZTEST001",
corpId: "corp-1",
});
expect(first.success).toBe(true);
expect(second.success).toBe(true);
expect(request.main).toHaveBeenCalledTimes(2);
});
test("preserves Tencent error details when an import returns HTTP 200 with FAIL", async () => {
request.main.mockResolvedValue({
ActionStatus: "FAIL",
ErrorCode: 70001,
ErrorInfo: "import failed",
});
const result = await tencentIM({
type: "importAccount",
account: "WZTEST002",
corpId: "corp-1",
});
expect(result).toMatchObject({
success: false,
message: "腾讯 IM 账号导入失败",
errorCode: 70001,
errorInfo: "import failed",
});
});
test("does not report sendmsg ErrorCode 20003 as success", async () => {
request.main.mockResolvedValue({
ActionStatus: "FAIL",
ErrorCode: 20003,
ErrorInfo: "Invalid sender or receiver identifier!",
});
const result = await tencentIM({
type: "sendSystemNotification",
formAccount: "WZTEST003",
toAccount: "000001",
SyncOtherMachine: 2,
corpId: "corp-1",
msgBody: [],
});
expect(result).toMatchObject({
success: false,
errorCode: 20003,
errorInfo: "Invalid sender or receiver identifier!",
});
});
test("returns a diagnostic error when IM configuration is incomplete", async () => {
getConfig.mockResolvedValue({});
const result = await tencentIM({
type: "importAccount",
account: "WZTEST004",
corpId: "corp-1",
});
expect(result).toMatchObject({
success: false,
message: "未找到完整的企业 IM 配置",
});
expect(request.main).not.toHaveBeenCalled();
});
test("returns a diagnostic error when UserSig generation fails", async () => {
TLSSigAPIv2.Api.mockImplementation(() => ({
genSig: jest.fn(() => {
throw new Error("signature failure");
}),
}));
const result = await tencentIM({
type: "importAccount",
account: "WZTEST005",
corpId: "corp-1",
});
expect(result).toMatchObject({
success: false,
message: "生成 UserSig 失败",
error: "signature failure",
});
expect(request.main).not.toHaveBeenCalled();
});
test("returns a diagnostic error on network failure", async () => {
request.main.mockRejectedValue(new Error("network unavailable"));
const result = await tencentIM({
type: "importAccount",
account: "WZTEST006",
corpId: "corp-1",
});
expect(result).toMatchObject({
success: false,
message: "腾讯 IM 账号导入失败",
error: "network unavailable",
});
});
});

View File

@ -49,6 +49,7 @@ const { jwtAuth, optionalJwtAuth } = require("./middleware/jwt-auth");
const { smartAuth } = require("./middleware/smart-auth"); const { smartAuth } = require("./middleware/smart-auth");
const sms = require("./sms"); const sms = require("./sms");
const path = require("path"); const path = require("path");
const { ObjectId } = require("mongodb");
const { useUploadFileParse } = require("./upload-file-parse"); const { useUploadFileParse } = require("./upload-file-parse");
const app = express(); const app = express();
const PORT = process.env.CONFIG_NODE_PORT; const PORT = process.env.CONFIG_NODE_PORT;
@ -215,13 +216,46 @@ app.get(
app.get( app.get(
"/zyt/2excel", "/zyt/2excel",
// jwtAuth, jwtAuth,
asyncHandler(async (req, res) => { asyncHandler(async (req, res) => {
try { try {
const requestData = req.query; const requestData = req.query;
const db = await getDatabase("Internet-hospital"); const db = await getDatabase("Internet-hospital");
const downloadParams = {
...req.query,
type: "downloadXlsx",
downloadType: req.query.type,
};
if (
downloadParams.downloadType === "downloadRx" &&
downloadParams.prescriptionType === "storeMedicinePurchase"
) {
let drugStoreId =
req.user && typeof req.user.drugStoreId === "string"
? req.user.drugStoreId.trim()
: "";
if (!drugStoreId && req.user && ObjectId.isValid(req.user.userId)) {
const corpDb = await getDatabase("corp");
const account = await corpDb.collection("corp-member").findOne(
{ _id: new ObjectId(req.user.userId) },
{ projection: { drugStoreId: 1 } }
);
drugStoreId =
account && typeof account.drugStoreId === "string"
? account.drugStoreId.trim()
: "";
}
if (!drugStoreId) {
return safeSend(res, {
success: false,
message: "当前账号未关联门店",
});
}
downloadParams.storeId = drugStoreId;
delete downloadParams.storeLoginNo;
}
const message = await hlw( const message = await hlw(
{ ...req.query, type: "downloadXlsx", downloadType: req.query.type }, downloadParams,
db, db,
res res
); );