feat: 功能调整
This commit is contained in:
parent
ea179d3bb3
commit
7fab382756
4
.env.dev
4
.env.dev
@ -56,8 +56,8 @@ CONFIG_EMS_SIGN_SECRET=ZmVRRjkyeDZnWHFnUGxkRQ==
|
||||
CONFIG_EMS_SENDER_NO=1100241354944
|
||||
|
||||
|
||||
## 震元his api
|
||||
CONFIG_ZYT_HIS_BASE_URL=http://115.239.161.48:8089
|
||||
## 海宁his api
|
||||
CONFIG_HN_HIS_BASE_URL=http://122.225.16.74:8026
|
||||
|
||||
## 银联支付请求基础路径
|
||||
CONFIG_UNION_PAY_BASE_URL=https://test-api-open.chinaums.com/v1
|
||||
|
||||
@ -111,28 +111,16 @@ async function addConsultOrder(item) {
|
||||
params.corpId = corpId;
|
||||
params.orderSource = "ALIPAY_MINI"; // 支付宝小程序
|
||||
params.doctorCode = params.doctorCode.trim();
|
||||
params.drugStoreId = 'HN000001';
|
||||
if (!params.drugStoreId) {
|
||||
return { success: false, message: "药房信息缺失" };
|
||||
}
|
||||
return await addOnlineConsultOrder(params);
|
||||
}
|
||||
|
||||
async function addOnlineConsultOrder(params) {
|
||||
try {
|
||||
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 { success, list, message } = await storeApi(
|
||||
{
|
||||
@ -200,6 +188,26 @@ async function addOnlineConsultOrder(params) {
|
||||
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);
|
||||
// 加密敏感字段
|
||||
const encryptedParams = encryptOrderFields(params);
|
||||
@ -207,6 +215,14 @@ async function addOnlineConsultOrder(params) {
|
||||
.collection("consult-order")
|
||||
.insertOne(encryptedParams);
|
||||
if (insertedId) {
|
||||
if (params.payStatus === 'success') {
|
||||
await startConsultOrder(params);
|
||||
}
|
||||
const res1 = await getConfig(params.corpId);
|
||||
const { autoAcceptOrder } = res1 || {};
|
||||
if (autoAcceptOrder) {
|
||||
await acceptConsultOrder(params);
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
message: "新增成功",
|
||||
@ -647,6 +663,9 @@ async function acceptConsultOrder(item) {
|
||||
) {
|
||||
return { success: false, message: "订单已过期,请刷新" };
|
||||
}
|
||||
if (item.orderStatus === 'processing') {
|
||||
return { success: true, message: "订单已处理" };
|
||||
}
|
||||
if (item.orderStatus !== "pending") {
|
||||
return { success: false, message: "订单已失效,请刷新" };
|
||||
}
|
||||
@ -917,6 +936,8 @@ async function getConsultOrder(item) {
|
||||
patientId,
|
||||
idCard,
|
||||
orderStatus,
|
||||
orderStatuses,
|
||||
payStatus,
|
||||
mergeStatus,
|
||||
doctorCode,
|
||||
corpId,
|
||||
@ -926,14 +947,56 @@ async function getConsultOrder(item) {
|
||||
orderSource,
|
||||
accountId,
|
||||
} = 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 (hospitalId) query.hospitalId = hospitalId;
|
||||
if (patientId) query.patientId = patientId;
|
||||
if (idCard) query.idCard = idCard;
|
||||
// if (corpId) query.corpId = corpId;
|
||||
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 (orderSource) query.orderSource = orderSource;
|
||||
if (typeof accountId === 'string' && accountId.trim()) query.accountId = accountId.trim();
|
||||
@ -959,7 +1022,7 @@ async function getConsultOrder(item) {
|
||||
// 先查询所有通过审核的诊断记录的 orderId
|
||||
const passedDiagnosticOrderIds = await db
|
||||
.collection("diagnostic-record")
|
||||
.distinct("orderId", { status: "PASS" });
|
||||
.distinct("orderId", { corpId, status: "PASS" });
|
||||
// 根据 hasPassDiagnostic 条件调整查询
|
||||
if (hasPassDiagnostic) {
|
||||
// 只查询有通过诊断的订单
|
||||
@ -1060,7 +1123,7 @@ async function getConsultOrder(item) {
|
||||
// 仍然需要根据诊断状态过滤
|
||||
const passedDiagnosticOrderIds = await db
|
||||
.collection("diagnostic-record")
|
||||
.distinct("orderId", { status: "PASS" });
|
||||
.distinct("orderId", { corpId, status: "PASS" });
|
||||
|
||||
if (hasPassDiagnostic) {
|
||||
// 只查询有通过诊断的订单
|
||||
@ -1114,7 +1177,7 @@ async function getConsultOrder(item) {
|
||||
// : query;
|
||||
// console.log("allQuery case2: ", allQuery);
|
||||
const allQuery = query;
|
||||
|
||||
console.log("allQuery case3333: ", allQuery);
|
||||
res = await db
|
||||
.collection("consult-order")
|
||||
.find(allQuery)
|
||||
|
||||
@ -118,4 +118,6 @@ interface DiagnosticRecord {
|
||||
|
||||
hisChargeStatus?: string // 配药订单处方的his支付状态 (在线购药的处方才有)
|
||||
needReUpload?: boolean // 门店处方 是否需要重新上传(可能存在的场景 调用his接口上传成功了 但是his未上传医保, 需要药师手动去his的上传处理页面上传)
|
||||
printCount?: number; // 门店自费处方打印次数
|
||||
lastPrintTime?: number; // 门店自费处方最近一次打印时间
|
||||
}
|
||||
@ -80,8 +80,12 @@ function decryptDiagnosticRecordList(list) {
|
||||
|
||||
exports.formatUploadRxData = function (data) {
|
||||
return {
|
||||
pay_type: data.feeType === 'YB' ? '1' : '0',
|
||||
patientid: data.patientId,
|
||||
orderno: data.medOrgOrderNo,
|
||||
med_type: data.med_type,
|
||||
dise_codg: data.dise_codg,
|
||||
dise_name: data.dise_name,
|
||||
doctorcode: data.doctorCode,
|
||||
unitcode: data.unitCode,
|
||||
hospid: "108",
|
||||
@ -92,9 +96,10 @@ exports.formatUploadRxData = function (data) {
|
||||
prsc_time: dayjs(data.prescriptionEndTime).format("YYYY-MM-DD HH:mm:ss"),
|
||||
details: data.drugs.map((i) => {
|
||||
const item = {
|
||||
drug_id: i.insurance_code,
|
||||
erp_id: i.erpId,
|
||||
drug_id: i.insurance_code || i._id,
|
||||
drug_name: i.drugName,
|
||||
drug_dosform: "",
|
||||
drug_dosform: i.dosage_form,
|
||||
drug_spec: i.specification || "",
|
||||
drug_pric: "0",
|
||||
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_dosunt: i.dosage_unit_code,
|
||||
unit: i.unit,
|
||||
appr_flag: i.forceSelfPay === 'Y' ? '2':'1'
|
||||
appr_flag: i.forceSelfPay === 'Y' ? '2' : '1'
|
||||
}
|
||||
if (item.appr_flag === '2') {
|
||||
//[his要求]当appr_flag=2时必填,如果这个药品维护了限制使用范围的,传1,否则就传2
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
let db = "";
|
||||
const { ObjectId } = require("mongodb");
|
||||
const { getDatabase } = require("../../mongodb");
|
||||
const dayjs = require("dayjs");
|
||||
const { decryptOrderList } = require("../consult-order/format");
|
||||
const {
|
||||
@ -12,20 +13,13 @@ const {
|
||||
decryptDiagnosticRecordList,
|
||||
buildHybridSearchQuery,
|
||||
} = require("./format");
|
||||
const zytHis = require("../zyt-his");
|
||||
const hnHis = require("../hn-his");
|
||||
const consultOrder = require("../consult-order");
|
||||
const tencentIM = require("../tencent-im");
|
||||
const beijingCa = require("../beijing-ca");
|
||||
const BigNumber = require("bignumber.js");
|
||||
const timeApi = require("../hlw-time-duration");
|
||||
const types = require("./types");
|
||||
const { getConfig } = require("../utils/config");
|
||||
|
||||
const PrescriptionTypeMap = {
|
||||
onlineMedicinePurchase: 'onlineMedicinePurchase',
|
||||
onlineConsult: 'storeMedicinePurchase',
|
||||
wineConsult: 'medicalWinePurchase'
|
||||
}
|
||||
|
||||
/**
|
||||
* 性能优化建议:
|
||||
@ -83,7 +77,7 @@ const status = {
|
||||
invalid: "INVALID", //处方作废 (目前就订单退费会导致处方作废)
|
||||
};
|
||||
|
||||
module.exports = async (item, mongodb) => {
|
||||
module.exports = async (item, mongodb, user) => {
|
||||
db = mongodb;
|
||||
switch (item.type) {
|
||||
case "addConsultDiagnosis":
|
||||
@ -93,7 +87,9 @@ module.exports = async (item, mongodb) => {
|
||||
case "getDiagnosisById":
|
||||
return await getDiagnosisById(item, db);
|
||||
case "getStoreDiagnosisRecord":
|
||||
return await getStoreDiagnosisRecord(item, db);
|
||||
return await getStoreDiagnosisRecord(item, db, user);
|
||||
case "markStoreRxPrinted":
|
||||
return await markStoreRxPrinted(item, db, user);
|
||||
case "getAuditDiagnosisList":
|
||||
return await getAuditDiagnosisList(item, db);
|
||||
case "getAuditedDiagnosisList":
|
||||
@ -164,18 +160,20 @@ async function addConsultDiagnosis(item) {
|
||||
const timeData = { type: 'addConsultDiagnosis', startTime: dayjs().format('YYYY-MM-DD HH:mm:ss'), orderId }
|
||||
try {
|
||||
const order = await db.collection("consult-order").findOne(
|
||||
{ orderId },
|
||||
{ orderId, corpId },
|
||||
{
|
||||
projection: {
|
||||
corpId: 1,
|
||||
createTime: 1,
|
||||
orderStatus: 1,
|
||||
registerId: 1,
|
||||
medorg_order_no: 1,
|
||||
accountId: 1,
|
||||
prescriptionStartTime: 1,
|
||||
consultType: 1,
|
||||
areaCode: 1,
|
||||
pickUpType: 1,
|
||||
medInfo: 1,
|
||||
feeType: 1,
|
||||
sex: 1,
|
||||
age: 1
|
||||
},
|
||||
@ -192,15 +190,9 @@ async function addConsultDiagnosis(item) {
|
||||
return { success: false, message: "当前订单不在有效期内" };
|
||||
}
|
||||
timeApi({ type: "addDoctorRxDuration", orderId }, db);
|
||||
// 提交诊断前, 先判单CA 自动签名是否失效
|
||||
const res = await conditionalCallBeijingCa({
|
||||
type: "autoSignVerifyCA",
|
||||
code: doctorCode,
|
||||
});
|
||||
timeData.caSignVerifyTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||
const doctorCAUserId = res.userId;
|
||||
if (!res.success) {
|
||||
return { success: false, message: "CA自动签名已失效, 请开启自动签" };
|
||||
const doctor = await db.collection("hlw-doctor").findOne({ corpId, doctorNo: doctorCode });
|
||||
if (!doctor) {
|
||||
return { success: false, message: "医生不存在" };
|
||||
}
|
||||
// 判断是否医生发的消息是否大于2条
|
||||
const chatCount = await db.collection("im-chat-msg").countDocuments({
|
||||
@ -228,21 +220,7 @@ async function addConsultDiagnosis(item) {
|
||||
if (record && record.status === status.pass) {
|
||||
return { success: false, message: "该订单已审核通过,请勿重复添加" };
|
||||
}
|
||||
if (order.consultType === "onlineMedicinePurchase") {
|
||||
const { success, message, drugs } = await getMedicinesWithHisChargeInfo(params.drugs, order.areaCode);
|
||||
if (!success) {
|
||||
return { success, message }
|
||||
}
|
||||
params.drugs = drugs;
|
||||
}
|
||||
if (order.consultType === "wineConsult") {
|
||||
const { success, message, wines } = await getWinesWithHisChargeInfo(params.wines);
|
||||
if (!success) {
|
||||
return { success, message }
|
||||
}
|
||||
params.wines = wines;
|
||||
params.pickUpType = order.pickUpType;
|
||||
}
|
||||
|
||||
if (record && record.status === status.unpass) {
|
||||
return await rewriteConsultDiagnosis(
|
||||
params,
|
||||
@ -258,30 +236,30 @@ async function addConsultDiagnosis(item) {
|
||||
success,
|
||||
message,
|
||||
data: pharmacist,
|
||||
} = await getRecommendPharmacist({ type: order.consultType === 'wineConsult' ? 'chinese' : 'west' });
|
||||
} = await getRecommendPharmacist({ corpId });
|
||||
timeData.pharmacistQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||
if (!success) {
|
||||
return { success: false, message };
|
||||
}
|
||||
const common = require('../../common')
|
||||
const data = {
|
||||
...params,
|
||||
createTime: Date.now(),
|
||||
status: status.init,
|
||||
pharmacistNo: pharmacist.pharmacistNo,
|
||||
pharmacist: pharmacist.pharmacistName,
|
||||
doctorCAUserId,
|
||||
doctorSignImg: doctor.signatureUrl,
|
||||
sex: order.sex,
|
||||
age: order.age,
|
||||
prescriptionType: PrescriptionTypeMap[order.consultType],
|
||||
// prescriptionType: PrescriptionTypeMap[order.consultType],
|
||||
accountId: order.accountId,
|
||||
medOrgOrderNo: order.registerId || order.medorg_order_no,
|
||||
areaCode: order.areaCode,
|
||||
med_type: order.medInfo.med_type,
|
||||
dise_codg: order.medInfo.dise_codg,
|
||||
dise_name: order.medInfo.dise_name,
|
||||
feeType: order.feeType,
|
||||
corpId,
|
||||
};
|
||||
if (order.consultType === 'wineConsult') {
|
||||
data.medOrgOrderNo = common.generateUniqueStr('R')
|
||||
}
|
||||
|
||||
// 由后端统一设置处方时间:
|
||||
// - 开始时间从 consult-order 读取(接单时已由后端写入)
|
||||
// - 结束时间为首次提交医嘱的服务器时间
|
||||
@ -317,14 +295,7 @@ async function addConsultDiagnosis(item) {
|
||||
await db.collection('doctor-last-submit-time').insertOne({ doctorCode: data.doctorCode, submitTime: Date.now() });
|
||||
}
|
||||
}
|
||||
if (['onlineMedicinePurchase', 'medicalWinePurchase'].includes(data.prescriptionType)) {
|
||||
const { medicinePurchaseRxDuration } = await getConfig(corpId);
|
||||
if (Number.isInteger(medicinePurchaseRxDuration) && medicinePurchaseRxDuration >= 10) {
|
||||
data.expireTime = dayjs().add(medicinePurchaseRxDuration, 'minute').valueOf();
|
||||
} else {
|
||||
return { success: false, message: "配药处方持续时长配置不正确, 请检查配置(最小10分钟)" };
|
||||
}
|
||||
}
|
||||
// return { success: false, message: '不让提交', data }
|
||||
// 加密敏感字段
|
||||
const encryptedData = encryptDiagnosticRecordFields(data);
|
||||
const { insertedId } = await db
|
||||
@ -351,7 +322,6 @@ async function addConsultDiagnosis(item) {
|
||||
success: true,
|
||||
message: "新增成功",
|
||||
data: insertedId,
|
||||
doctorCAUserId,
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
@ -468,147 +438,94 @@ async function getOrderDiagnosis(item, db) {
|
||||
}
|
||||
}
|
||||
|
||||
async function getStoreDiagnosisRecord(item, db) {
|
||||
if (typeof item.storeId !== "string" || item.storeId.trim() === "") {
|
||||
async function resolveStoreIds(item, db, user) {
|
||||
let authorizedStoreId =
|
||||
user && typeof user.drugStoreId === "string" ? user.drugStoreId.trim() : "";
|
||||
if (!authorizedStoreId && user && ObjectId.isValid(user.userId)) {
|
||||
const corpDb = await getDatabase("corp");
|
||||
const account = await corpDb.collection("corp-member").findOne(
|
||||
{ _id: new ObjectId(user.userId) },
|
||||
{ projection: { drugStoreId: 1 } }
|
||||
);
|
||||
authorizedStoreId =
|
||||
account && typeof account.drugStoreId === "string"
|
||||
? account.drugStoreId.trim()
|
||||
: "";
|
||||
}
|
||||
if (!authorizedStoreId) {
|
||||
return [];
|
||||
}
|
||||
const storeIds = [authorizedStoreId];
|
||||
const store = await db.collection("store-list").findOne(
|
||||
{ store_id: authorizedStoreId },
|
||||
{ projection: { login_no: 1 } }
|
||||
);
|
||||
if (store && typeof store.login_no === "string" && store.login_no.trim()) {
|
||||
storeIds.push(store.login_no.trim());
|
||||
}
|
||||
return [...new Set(storeIds)];
|
||||
}
|
||||
|
||||
async function getStoreDiagnosisRecord(item, db, user) {
|
||||
const storeIds = await resolveStoreIds(item, db, user);
|
||||
if (storeIds.length === 0) {
|
||||
return { success: false, message: "参数错误" };
|
||||
}
|
||||
const query = {
|
||||
drugStoreNo: { $in: [...new Set(storeIds)] },
|
||||
status: status.pass,
|
||||
};
|
||||
const page = item.page > 0 && Number.isInteger(item.page) ? item.page : 1;
|
||||
const pageSize =
|
||||
item.pageSize > 0 && Number.isInteger(item.pageSize) ? item.pageSize : 20;
|
||||
const date =
|
||||
item.date && dayjs(item.date).isValid() ? dayjs(item.date) : dayjs();
|
||||
const createTime = {
|
||||
$gte: date.startOf("day").valueOf(),
|
||||
$lte: date.endOf("day").valueOf(),
|
||||
};
|
||||
const query = {
|
||||
drugStoreNo: item.storeId,
|
||||
orderSource: "MATEPAD",
|
||||
status: status.pass,
|
||||
createTime,
|
||||
};
|
||||
if (typeof item.keyword == "string" && item.keyword.trim() !== "") {
|
||||
// 使用混合搜索支持明文和密文数据
|
||||
const nameCondition = buildHybridSearchQuery("name", item.keyword, false);
|
||||
const orConditions = [
|
||||
{ doctorName: { $regex: item.keyword.trim(), $options: "i" } },
|
||||
];
|
||||
|
||||
// 如果构建了name搜索条件,添加到$or中
|
||||
if (nameCondition && nameCondition.$or) {
|
||||
orConditions.push(...nameCondition.$or);
|
||||
} else if (nameCondition) {
|
||||
orConditions.push(nameCondition);
|
||||
const startDate = item.startDate && dayjs(item.startDate).isValid() ? dayjs(item.startDate) : null;
|
||||
const endDate = item.endDate && dayjs(item.endDate).isValid() ? dayjs(item.startDate) : null;
|
||||
if (startDate) {
|
||||
query.createTime = { $gte: startDate.startOf("day").valueOf() }
|
||||
}
|
||||
if (endDate) {
|
||||
query.createTime = query.createTime || {};
|
||||
query.createTime['$lte'] = endDate.endOf("day").valueOf();
|
||||
}
|
||||
|
||||
query.$or = orConditions;
|
||||
const nameKeyword =
|
||||
typeof item.name === "string" ? item.name.trim().toLocaleLowerCase() : "";
|
||||
if (typeof item.medOrgOrderNo === "string" && item.medOrgOrderNo.trim()) {
|
||||
const keyword = item.medOrgOrderNo.trim().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
query.medOrgOrderNo = { $regex: keyword, $options: "i" };
|
||||
}
|
||||
if (["YB", "ZF"].includes(item.feeType)) {
|
||||
query.feeType = item.feeType;
|
||||
}
|
||||
try {
|
||||
if (nameKeyword) {
|
||||
// 姓名是加密字段,密文无法直接进行模糊查询。
|
||||
// 先读取当前筛选范围内的姓名和 ID,解密后匹配,再用 ID 正常分页。
|
||||
const nameCandidates = await db
|
||||
.collection("diagnostic-record")
|
||||
.find(query, { projection: { name: 1 } })
|
||||
.toArray();
|
||||
const matchedIds = decryptDiagnosticRecordList(nameCandidates)
|
||||
.filter(
|
||||
(record) =>
|
||||
typeof record.name === "string" &&
|
||||
record.name.toLocaleLowerCase().includes(nameKeyword)
|
||||
)
|
||||
.map((record) => record._id);
|
||||
query._id = { $in: matchedIds };
|
||||
}
|
||||
const total = await db
|
||||
.collection("diagnostic-record")
|
||||
.countDocuments(query);
|
||||
const list = await db
|
||||
.collection("diagnostic-record")
|
||||
.find(query)
|
||||
.sort({ auditTime: -1 })
|
||||
.sort({ createTime: -1, _id: -1 })
|
||||
.skip((page - 1) * pageSize)
|
||||
.limit(pageSize)
|
||||
.toArray();
|
||||
// 查询今日的处方 对已上传但未获取 his处方上传状态的处方进行查询
|
||||
const start = dayjs().startOf("day").valueOf();
|
||||
const toSignList = list
|
||||
.filter(
|
||||
(i) =>
|
||||
i.patientId &&
|
||||
i.medOrgOrderNo &&
|
||||
i.status === status.pass &&
|
||||
i.uploadStatus === "uploaded" &&
|
||||
!i.hisUploadStatus &&
|
||||
i.createTime > start
|
||||
)
|
||||
.map((i) => ({
|
||||
_id: i._id,
|
||||
patientId: i.patientId,
|
||||
medOrgOrderNo: i.medOrgOrderNo,
|
||||
}));
|
||||
if (toSignList.length > 0) {
|
||||
const task = toSignList.map((data) =>
|
||||
zytHis({
|
||||
type: "getHisPrescriptionUploadStatus",
|
||||
orderno: data.medOrgOrderNo,
|
||||
patientId: data.patientId,
|
||||
_id: data._id,
|
||||
})
|
||||
);
|
||||
const res = await Promise.all(task);
|
||||
const result = res.reduce(
|
||||
(acc, item) => {
|
||||
if (item.hisUploadStatus === "uploaded") {
|
||||
acc.uploaded.push(item._id);
|
||||
} else if (item.hisUploadStatus === "error") {
|
||||
acc.uploadError.push(item._id);
|
||||
} else if (item.hisUploadStatus === "notUploaded") {
|
||||
acc.notUploaded.push(item._id);
|
||||
}
|
||||
if (item.hisUploadStatus && item.hisUploadStatus !== "notUploaded") {
|
||||
acc.map[item.orderno] = item.hisUploadStatus;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ uploaded: [], uploadError: [], notUploaded: [], map: {} }
|
||||
);
|
||||
if (result.uploaded.length > 0) {
|
||||
await db
|
||||
.collection("diagnostic-record")
|
||||
.updateMany(
|
||||
{ _id: { $in: result.uploaded } },
|
||||
{ $set: { hisUploadStatus: "uploaded" } }
|
||||
);
|
||||
}
|
||||
if (result.uploadError.length > 0) {
|
||||
await db
|
||||
.collection("diagnostic-record")
|
||||
.updateMany(
|
||||
{ _id: { $in: result.uploadError } },
|
||||
{ $set: { hisUploadStatus: "error" } }
|
||||
);
|
||||
}
|
||||
if (result.notUploaded.length > 0) {
|
||||
await db
|
||||
.collection("diagnostic-record")
|
||||
.updateMany(
|
||||
{ _id: { $in: result.notUploaded }, needReUpload: { $exists: false } },
|
||||
{ $set: { needReUpload: true } }
|
||||
);
|
||||
}
|
||||
list.forEach((i) => {
|
||||
if (result.map[i.orderId]) {
|
||||
i.hisUploadStatus = result.map[i.orderId];
|
||||
}
|
||||
});
|
||||
}
|
||||
// 解密敏感字段
|
||||
|
||||
const decryptedList = decryptDiagnosticRecordList(list);
|
||||
const wineList = decryptedList.filter(i => i.prescriptionType === 'medicalWinePurchase');
|
||||
if (wineList.length) {
|
||||
const wines = await db.collection('chinese-medicine-order').find({ rpNo: { $in: wineList.map(i => i._id.toString()) } }).toArray();
|
||||
for (let item of wineList) {
|
||||
if (item.pickUpType === 'selfTake') {
|
||||
item.wineStatus = 'completed';
|
||||
continue;
|
||||
}
|
||||
const wine = wines.find(i => i.rpNo === item._id.toString());
|
||||
if (wine) {
|
||||
item.wineStatus = wine.status;
|
||||
continue;
|
||||
}
|
||||
if (item.expireTime > Date.now()) {
|
||||
item.wineStatus = 'unpay';
|
||||
} else {
|
||||
item.wineStatus = 'expired';
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
message: "查询成功",
|
||||
@ -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) {
|
||||
const page = item.page > 0 && Number.isInteger(item.page) ? item.page : 1;
|
||||
const pageSize =
|
||||
@ -951,27 +899,25 @@ async function getAuditedDiagnosisList(item, db) {
|
||||
}
|
||||
|
||||
async function auditDiagnosis(item, db) {
|
||||
const { pharmacistNo, pharmacist } = item;
|
||||
const timeData = { type: 'auditDiagnosis', startTime: dayjs().format('YYYY-MM-DD HH:mm:ss') };
|
||||
const res = await conditionalCallBeijingCa({
|
||||
type: "autoSignVerifyCA",
|
||||
code: pharmacistNo,
|
||||
}, db);
|
||||
timeData.caSignVerifyTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||
if (!res.success) {
|
||||
return { success: false, message: "CA自动签名已失效, 请开启自动签" };
|
||||
// const { pharmacistNo, pharmacist, corpId } = item;
|
||||
const pharmacistNo = item && typeof item.pharmacistNo === 'string' ? item.pharmacistNo : '';
|
||||
const corpId = item && typeof item.corpId === 'string' ? item.corpId : '';
|
||||
if (!corpId || !pharmacistNo) {
|
||||
return { success: false, message: "参数错误" };
|
||||
}
|
||||
const pharmacistCAUserId = res.userId;
|
||||
if (![status.pass, status.unpass].includes(item.status)) {
|
||||
return { success: false, message: "状态错误" };
|
||||
}
|
||||
if (!pharmacistNo || !pharmacist) {
|
||||
return { success: false, message: "审方药师信息不能为空" };
|
||||
}
|
||||
const ids = Array.isArray(item.ids) ? item.ids.filter(i => typeof i === 'string' && i.trim() !== '') : [];
|
||||
if (ids.length === 0) {
|
||||
return { success: false, message: "id错误" };
|
||||
}
|
||||
const 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)
|
||||
? item.reasons.filter((i) => typeof i === "string" && i.trim() !== "")
|
||||
: [];
|
||||
@ -982,7 +928,6 @@ async function auditDiagnosis(item, db) {
|
||||
// 1. 根据待审核id 查询待处理的处方记录
|
||||
const _rxList = await db.collection("diagnostic-record").find(
|
||||
{ _id: { $in: ids.map((id) => new ObjectId(id)) }, status: status.init, pharmacistNo },
|
||||
// { projection: { orderId: 1, _id: 1, name: 1, sex: 1, age: 1, drugs: 1, prescriptionType: 1, diagnosisList: 1 } }
|
||||
).toArray();
|
||||
// 患者姓名 证件号 手机号解密
|
||||
const rxList = _rxList.map(i => decryptDiagnosticRecordFields(i));
|
||||
@ -1101,21 +1046,10 @@ async function auditDiagnosis(item, db) {
|
||||
|
||||
|
||||
async function uploadRx2His(rx, order) {
|
||||
const hisUploadFn = {
|
||||
storeMedicinePurchase: 'hlwuploadprescription',
|
||||
onlineMedicinePurchase: 'uploadMedicinePurchaseRx',
|
||||
medicalWinePurchase: 'uploadWinePurchaseRx',
|
||||
}
|
||||
const formatFn = {
|
||||
storeMedicinePurchase: formatUploadRxData,
|
||||
onlineMedicinePurchase: formatOnlineMedicinePurchaseData,
|
||||
medicalWinePurchase: formatWinePurchaseData,
|
||||
}
|
||||
try {
|
||||
const uploadData = formatFn[rx.prescriptionType](rx, order);
|
||||
const type = hisUploadFn[rx.prescriptionType];
|
||||
const { success, message } = await zytHis({
|
||||
type,
|
||||
const uploadData = formatUploadRxData(rx, order);
|
||||
const { success, message } = await hnHis({
|
||||
type: 'hlwuploadprescription',
|
||||
data: uploadData
|
||||
})
|
||||
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 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([
|
||||
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),
|
||||
@ -1875,7 +1809,7 @@ async function getAccountRxList(ctx) {
|
||||
await getHisPrescriptionUploadStatus(list);
|
||||
// 获取处方单号
|
||||
const rpNos = list.filter(i => i.prescriptionType === 'onlineMedicinePurchase').map(i => i._id.toString());
|
||||
const orderList = await db.collection("medicine-purchase-order").find({ rpNo: { $in: rpNos } }, { projection: types.output.medicinePurchaseOrder }).toArray();
|
||||
const orderList = await db.collection("medicine-purchase-order").find({ corpId: ctx.corpId, rpNo: { $in: rpNos } }, { projection: types.output.medicinePurchaseOrder }).toArray();
|
||||
return { success: true, message: "查询成功", orderList, list: decryptDiagnosticRecordList(list), total, pages: Math.ceil(total / pageSize), now: Date.now() };
|
||||
} catch (e) {
|
||||
return { success: false, message: `获取处方记录失败:${e?.message || '未知错误 '}` };
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
const dayjs = require("dayjs");
|
||||
const ExcelJS = require('exceljs');
|
||||
const Sm4Util = require("../../utils/sm4-util");
|
||||
const { buildHybridSearchQuery } = require("../diagnostic-record/format");
|
||||
|
||||
module.exports = async (item, db, res) => {
|
||||
switch (item.downloadType) {
|
||||
@ -87,6 +88,7 @@ async function downloadStream(db, res, options) {
|
||||
|
||||
async function downloadRx(ctx, db, res) {
|
||||
const { startDate, endDate, startAuditDate, endAuditDate } = ctx;
|
||||
const isStoreRxExport = ctx.prescriptionType === "storeMedicinePurchase";
|
||||
const hasDates = startDate && dayjs(startDate).isValid() && endDate && dayjs(endDate).isValid();
|
||||
const hasAuditDate = startAuditDate && dayjs(startAuditDate).isValid() && endAuditDate && dayjs(endAuditDate).isValid();
|
||||
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() };
|
||||
}
|
||||
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') {
|
||||
query.doctorCode = ctx.doctorCode.trim();
|
||||
@ -105,8 +108,47 @@ async function downloadRx(ctx, db, res) {
|
||||
if (typeof ctx.pharmacistNo === 'string' && 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: 'reasons', width: 10 },
|
||||
{ header: '提交时间', key: 'createTime', width: 20 },
|
||||
@ -117,7 +159,7 @@ async function downloadRx(ctx, db, res) {
|
||||
{ header: '诊断', key: 'diagnosisList', width: 10 },
|
||||
{ header: '药品', key: 'drugs', width: 10 },
|
||||
];
|
||||
let filename = '处方记录';
|
||||
let filename = isStoreRxExport ? '门店处方记录' : '处方记录';
|
||||
if (hasDates) {
|
||||
filename += `_${dayjs(startDate).format('YYYY-MM-DD')}_${dayjs(endDate).format('YYYY-MM-DD')}`;
|
||||
} else if (hasAuditDate) {
|
||||
@ -139,10 +181,21 @@ async function downloadRx(ctx, db, res) {
|
||||
collection: 'diagnostic-record',
|
||||
aggregateList: [
|
||||
{ $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 } },
|
||||
],
|
||||
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,
|
||||
reasons: Array.isArray(i.reasons) ? i.reasons.join(",") : "",
|
||||
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,
|
||||
name: Sm4Util.decryptDataForSm3(i.name)|| i.name,
|
||||
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);
|
||||
|
||||
@ -6,8 +6,7 @@ interface Drug {
|
||||
_id: ObjectId; // 数据库中的唯一标识符
|
||||
pinyin_code: string; // 拼音码
|
||||
name: string; // 药品名称
|
||||
product_id?: number; // 产品ID
|
||||
product_id_str?: string; // 产品ID的字符串形式
|
||||
erpId: string; // ERP ID
|
||||
specification: string; // 药品规格
|
||||
package_amount: number; // 包装数量
|
||||
manufacturer: string; // 生产厂家
|
||||
|
||||
@ -3,6 +3,7 @@ const { ObjectId } = require("mongodb");
|
||||
|
||||
const { getDrugList } = require('./verify');
|
||||
let db = "";
|
||||
|
||||
module.exports = async (item, mongodb) => {
|
||||
db = mongodb;
|
||||
switch (item.type) {
|
||||
@ -54,6 +55,7 @@ async function getDrugInfo(item) {
|
||||
{ generic_name: new RegExp(keyword.trim(), "i") },
|
||||
{ barcode: keyword.trim() },
|
||||
{ pinyin_code: new RegExp(keyword.trim(), "i") },
|
||||
{ erpId: keyword.trim() },
|
||||
{ product_id_str: keyword.trim() },
|
||||
];
|
||||
fuzzyQuery = { $or: arr };
|
||||
@ -128,7 +130,7 @@ async function updateDrugInfo(item) {
|
||||
//新增药品信息
|
||||
/**
|
||||
* pinyin_code:拼音操作码
|
||||
product_id:药店货品ID
|
||||
erpId:ERP ID
|
||||
name:名称
|
||||
specification:规格
|
||||
manufacturer:厂家
|
||||
@ -177,8 +179,11 @@ async function getHlwDrugInfo(ctx) {
|
||||
if (typeof ctx.onSale === "boolean") {
|
||||
query.onSale = ctx.onSale
|
||||
}
|
||||
if (typeof ctx.productId === 'string' && ctx.productId.trim()) {
|
||||
query.product_id_str = ctx.productId.trim()
|
||||
if (typeof ctx.erpId === 'string' && ctx.erpId.trim()) {
|
||||
query.$or = [
|
||||
{ erpId: ctx.erpId.trim() },
|
||||
{ product_id_str: ctx.erpId.trim() }
|
||||
]
|
||||
}
|
||||
if (typeof ctx.barcode === 'string' && ctx.barcode.trim()) {
|
||||
query.barcode = ctx.barcode.trim()
|
||||
@ -239,6 +244,20 @@ async function editHlwDrugInfo(ctx) {
|
||||
}
|
||||
try {
|
||||
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 = {};
|
||||
if (Object.keys(data).length) {
|
||||
const peopleInfo = {
|
||||
@ -366,7 +385,12 @@ async function getStoreSimilarDrugInfo(ctx) {
|
||||
}))
|
||||
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) {
|
||||
return { success: false, message: e.message }
|
||||
}
|
||||
@ -382,6 +406,7 @@ async function searchDrugs(ctx) {
|
||||
$or: [
|
||||
{ name: { $regex: keyword, $options: "i" } },
|
||||
{ pinyin_code: keyword },
|
||||
{ erpId: keyword },
|
||||
{ product_id_str: keyword },
|
||||
{ barcode: keyword },
|
||||
{ insurance_code: keyword }
|
||||
|
||||
@ -16,7 +16,8 @@ const drugFieldDict = [
|
||||
{ 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: '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_pinyin', verify: (val, label) => validator.verifyString(val, label) },
|
||||
// 仅在线药品库使用:管理分类(处方药 / OTC)与剂型
|
||||
@ -47,9 +48,10 @@ function validateNumber(val, label, len = 0, required = false) {
|
||||
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 => {
|
||||
const res = drugFieldDict.reduce((obj, item) => {
|
||||
const res = fieldDict.reduce((obj, item) => {
|
||||
const key = item.field;
|
||||
if (!verifyEveryField && !(key in drug)) {
|
||||
return obj
|
||||
@ -64,7 +66,7 @@ function getDrugList(arr, verifyEveryField = true, extraInfo = {}) {
|
||||
}
|
||||
return obj
|
||||
}, { msgs: [] })
|
||||
if ('product_id' in res) {
|
||||
if (libType === 'online' && 'product_id' in res) {
|
||||
res.product_id_str = res.product_id.toString();
|
||||
}
|
||||
if ('frequency' in res) {
|
||||
@ -74,12 +76,12 @@ function getDrugList(arr, verifyEveryField = true, extraInfo = {}) {
|
||||
})
|
||||
}
|
||||
|
||||
function getDrugFromChineseKeyObj(data) {
|
||||
function getDrugFromChineseKeyObj(data, libType = 'hlw') {
|
||||
const rowData = {};
|
||||
Object.keys(data).forEach(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] ?? '';
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
@ -17,6 +17,7 @@ interface HlwDoctor {
|
||||
job: Job;
|
||||
offlineTime?: number; // 上次下线时间
|
||||
testDoctor: boolean; // 是否为测试医生
|
||||
signatureUrl?: string; // 医生签名图片地址
|
||||
openWestPres: boolean; // 是否开启西药处方
|
||||
openChinesePres: boolean; // 是否开启中药处方
|
||||
}
|
||||
|
||||
@ -69,6 +69,10 @@ const rules = [
|
||||
// 是否开通视频问诊功能:允许 true / false,默认 false
|
||||
validate: val => validator.verifyListItem(val, [true, false], false, '视频问诊功能', false)
|
||||
},
|
||||
{
|
||||
key: 'signatureUrl',
|
||||
validate: val => validator.verifyString(val, '签名图片', 0, 500)
|
||||
},
|
||||
{
|
||||
key: 'openWestPres',
|
||||
validate: val => validator.verifyListItem(val, [true, false], false, '西医处方', false)
|
||||
|
||||
@ -598,10 +598,7 @@ async function setPharmacistOnlineStatus(ctx) {
|
||||
if (res.matchedCount === 0) {
|
||||
return { success: false, message: "药师不存在或不是药师" }
|
||||
}
|
||||
if (res.modifiedCount === 1) {
|
||||
return { success: true, message: "在线状态修改成功", data: ctx.onlineStatus }
|
||||
}
|
||||
return { success: false, message: "在线状态修改失败" }
|
||||
} catch (e) {
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
const { ObjectId } = require('mongodb');
|
||||
const validator = require('../../utils/validator');
|
||||
const Sm4Util = require('../../utils/sm4-util');
|
||||
const hnHis = require('../hn-his');
|
||||
|
||||
const COLLECTION_NAME = 'hlw-patient';
|
||||
|
||||
@ -55,6 +56,12 @@ function validatePatient(item) {
|
||||
|
||||
return {
|
||||
success: true,
|
||||
origin: {
|
||||
name,
|
||||
certNo,
|
||||
mobile,
|
||||
address,
|
||||
},
|
||||
data: {
|
||||
accountId: accountResult.accountId,
|
||||
anotherIdNo: Sm4Util.encryptDataForSm3(certNo),
|
||||
@ -78,6 +85,16 @@ async function addHlwPatient(item, db) {
|
||||
if (exists > 0) {
|
||||
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({
|
||||
...validation.data,
|
||||
|
||||
@ -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
942
hlw/hn-his/index.js
Normal 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
220
hlw/hn-his/notify.js
Normal 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
54
hlw/hn-his/types.ts
Normal 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
|
||||
}
|
||||
|
||||
|
||||
14
hlw/index.js
14
hlw/index.js
@ -1,4 +1,4 @@
|
||||
const zytHis = require("./zyt-his");
|
||||
const hnHis = require("./hn-his");
|
||||
const drugInfo = require("./drug-info");
|
||||
const patientDescription = require("./patient-description");
|
||||
const tencentIM = require("./tencent-im");
|
||||
@ -27,8 +27,9 @@ const miniAppLog = require("./mini-app-log");
|
||||
const chineseMedicine = require("./chinese-medicine");
|
||||
const chineseMedicineOrder = require("./chinese-medicine-order");
|
||||
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) {
|
||||
case "getHisCustomer":
|
||||
case "addHisCustomer":
|
||||
@ -42,7 +43,7 @@ module.exports = async (item, db, res) => {
|
||||
case "getHisInvoice":
|
||||
case "getHisEmployeeInfo":
|
||||
case "getWineDrugStockInfo":
|
||||
return await zytHis(item, db);
|
||||
return await hnHis(item, db);
|
||||
case "getDrugInfo":
|
||||
case "deleteDrugInfo":
|
||||
case "updateDrugInfo":
|
||||
@ -129,6 +130,7 @@ module.exports = async (item, db, res) => {
|
||||
case "getLatestSubmitTime":
|
||||
case "orderHasDiagnosis":
|
||||
case "getStoreDiagnosisRecord":
|
||||
case "markStoreRxPrinted":
|
||||
case "getAllDiagnosisRecord":
|
||||
case "getDoctorRxStats":
|
||||
case "getPharmacistRxStats":
|
||||
@ -145,7 +147,7 @@ module.exports = async (item, db, res) => {
|
||||
case "getDiagnosisById":
|
||||
case "getPatientWineBuyCount":
|
||||
case "getYbReviewResult":
|
||||
return diagnosticRecord(item, db);
|
||||
return diagnosticRecord(item, db, context);
|
||||
case "caAuth":
|
||||
case "getSealImage":
|
||||
case "getCaUserInfo":
|
||||
@ -183,7 +185,7 @@ module.exports = async (item, db, res) => {
|
||||
case "getRegPayStatus":
|
||||
return await hlwReg(item, db);
|
||||
case "downloadXlsx":
|
||||
return await downloadXlsx(item, db, res);
|
||||
return await downloadXlsx(item, db, context);
|
||||
case "getDoctorTimeStats":
|
||||
return await hlwTimeDuration(item, db);
|
||||
case "getHlwOrderConfig":
|
||||
@ -267,6 +269,8 @@ module.exports = async (item, db, res) => {
|
||||
case "updateHlwPatient":
|
||||
case "deleteHlwPatient":
|
||||
return await hlwPatient.main(item, db);
|
||||
case "saveScannedCard":
|
||||
return await scannedCardArchive(item, db);
|
||||
default:
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@ -26,7 +26,7 @@ async function getBatchInsertOnlineDrugFn() {
|
||||
rowData[key.trim()] = row[key];
|
||||
}
|
||||
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;
|
||||
if (msgs.length === 0) {
|
||||
batch.push(drug);
|
||||
|
||||
@ -181,7 +181,7 @@ async function addOnlineDrugInfo(ctx, db) {
|
||||
return { success: false, message: "ERP编码已存在" };
|
||||
}
|
||||
}
|
||||
const [item] = getDrugList([data], true, peopleInfo);
|
||||
const [item] = getDrugList([data], true, peopleInfo, 'online');
|
||||
const { msgs, ...drug } = item;
|
||||
if (msgs.length) {
|
||||
return { success: false, message: msgs.join(",") };
|
||||
@ -236,7 +236,7 @@ async function editOnlineDrugInfo(ctx, db) {
|
||||
typeof ctx.operatorId === "string" ? ctx.operatorId : "",
|
||||
updateTime: Date.now(),
|
||||
};
|
||||
const [item] = getDrugList([data], false, peopleInfo);
|
||||
const [item] = getDrugList([data], false, peopleInfo, 'online');
|
||||
const { msgs, ...drug } = item;
|
||||
if (msgs.length) {
|
||||
return { success: false, message: msgs.join(",") };
|
||||
@ -321,7 +321,7 @@ async function importOnlineDrugInfo(ctx, db) {
|
||||
typeof ctx.operatorId === "string" ? ctx.operatorId : "",
|
||||
createTime: Date.now(),
|
||||
};
|
||||
const datalist = getDrugList(drugs, true, peopleInfo);
|
||||
const datalist = getDrugList(drugs, true, peopleInfo, 'online');
|
||||
datalist.forEach((i) => {
|
||||
if (!i.his_drug_code || !String(i.his_drug_code).trim()) {
|
||||
i.msgs.push("ERP编码不能为空");
|
||||
|
||||
87
hlw/scanned-card-archive/index.js
Normal file
87
hlw/scanned-card-archive/index.js
Normal 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;
|
||||
74
hlw/scanned-card-archive/index.test.js
Normal file
74
hlw/scanned-card-archive/index.test.js
Normal 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();
|
||||
});
|
||||
});
|
||||
@ -14,6 +14,8 @@ module.exports = async (item, mongodb) => {
|
||||
return await setUserProfile(item);
|
||||
case "generateUserSig":
|
||||
return generateUserSig(item);
|
||||
case "importAccount":
|
||||
return await importAccount(item);
|
||||
case "deleteSession":
|
||||
return deleteSession(item);
|
||||
case "deleteFriend":
|
||||
@ -39,13 +41,17 @@ async function sendSystemNotification(item) {
|
||||
const { formAccount, toAccount, msgBody, SyncOtherMachine, corpId } = item;
|
||||
console.log("发送系统通知请求体msgBody:", msgBody);
|
||||
console.log("item", item);
|
||||
const { sdkAppId, identifier, secretKey } = await getConfig(corpId);
|
||||
const { data: userSig } = await generateUserSig({
|
||||
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 = {
|
||||
@ -57,10 +63,13 @@ async function sendSystemNotification(item) {
|
||||
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 {
|
||||
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");
|
||||
console.log("系统通知发送成功:", response.data);
|
||||
if (!isTencentIMSuccess(response)) {
|
||||
console.error("发送系统通知失败:", response);
|
||||
return createTencentIMFailure("系统通知发送失败", response);
|
||||
}
|
||||
console.log("系统通知发送成功:", response);
|
||||
return {
|
||||
success: true,
|
||||
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
|
||||
@ -330,59 +357,62 @@ async function deleteFriend(item) {
|
||||
}
|
||||
}
|
||||
|
||||
// 批量导入医生账号
|
||||
async function importDoctorAccount(event) {
|
||||
console.log("用户资料设置求体:", item);
|
||||
const { formAccount, profileItem, corpId } = item;
|
||||
const {
|
||||
sdkAppId,
|
||||
identifier,
|
||||
secretKey,
|
||||
} = await getConfig(corpId);
|
||||
if (!sdkAppId) {
|
||||
// 将业务账号导入腾讯 IM。重复导入同一账号是幂等操作。
|
||||
async function importAccount(item) {
|
||||
const { account, corpId } = item;
|
||||
if (typeof account !== "string" || !account.trim()) {
|
||||
return {
|
||||
success: false,
|
||||
message: "未找到企业配置",
|
||||
message: "IM 账号不能为空",
|
||||
};
|
||||
}
|
||||
const { data: userSig } = await generateUserSig({
|
||||
|
||||
try {
|
||||
const { sdkAppId, identifier, secretKey } = (await getConfig(corpId)) || {};
|
||||
const sigResult = await generateUserSig({
|
||||
userId: identifier,
|
||||
corpId,
|
||||
sdkAppId,
|
||||
secretKey,
|
||||
});
|
||||
// 构造请求 URL
|
||||
// 构造请求体
|
||||
if (!sigResult.success) {
|
||||
return sigResult;
|
||||
}
|
||||
|
||||
const data = {
|
||||
AccountList: [],
|
||||
Identifier: account.trim(),
|
||||
};
|
||||
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 {
|
||||
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");
|
||||
console.log("用户资料设置", response.data);
|
||||
if (!isTencentIMSuccess(response)) {
|
||||
console.error("腾讯 IM 账号导入失败:", response);
|
||||
return createTencentIMFailure("腾讯 IM 账号导入失败", response);
|
||||
}
|
||||
console.log("腾讯 IM 账号导入成功:", account);
|
||||
return {
|
||||
success: true,
|
||||
message: "用户资料设置成功",
|
||||
message: "腾讯 IM 账号导入成功",
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "用户资料设置失败",
|
||||
message: "腾讯 IM 账号导入失败",
|
||||
error: error.response ? error.response.data : error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function generateUserSig({ userId, corpId, sdkAppId, secretKey }) {
|
||||
try {
|
||||
if (!sdkAppId || !secretKey) {
|
||||
const res = await getConfig(corpId);
|
||||
const res = (await getConfig(corpId)) || {};
|
||||
sdkAppId = res.sdkAppId;
|
||||
secretKey = res.secretKey;
|
||||
}
|
||||
if (!sdkAppId) {
|
||||
if (!sdkAppId || !secretKey || !userId) {
|
||||
return {
|
||||
success: false,
|
||||
message: "未找到企业配置",
|
||||
message: "未找到完整的企业 IM 配置",
|
||||
};
|
||||
}
|
||||
var api = new TLSSigAPIv2.Api(sdkAppId, secretKey);
|
||||
@ -393,4 +423,11 @@ async function generateUserSig({ userId, corpId, sdkAppId, secretKey }) {
|
||||
data: sig,
|
||||
sdkAppId,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "生成 UserSig 失败",
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
178
hlw/tencent-im/index.test.js
Normal file
178
hlw/tencent-im/index.test.js
Normal 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",
|
||||
});
|
||||
});
|
||||
});
|
||||
38
index.js
38
index.js
@ -49,6 +49,7 @@ const { jwtAuth, optionalJwtAuth } = require("./middleware/jwt-auth");
|
||||
const { smartAuth } = require("./middleware/smart-auth");
|
||||
const sms = require("./sms");
|
||||
const path = require("path");
|
||||
const { ObjectId } = require("mongodb");
|
||||
const { useUploadFileParse } = require("./upload-file-parse");
|
||||
const app = express();
|
||||
const PORT = process.env.CONFIG_NODE_PORT;
|
||||
@ -215,13 +216,46 @@ app.get(
|
||||
|
||||
app.get(
|
||||
"/zyt/2excel",
|
||||
// jwtAuth,
|
||||
jwtAuth,
|
||||
asyncHandler(async (req, res) => {
|
||||
try {
|
||||
const requestData = req.query;
|
||||
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(
|
||||
{ ...req.query, type: "downloadXlsx", downloadType: req.query.type },
|
||||
downloadParams,
|
||||
db,
|
||||
res
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user