2340 lines
80 KiB
JavaScript
2340 lines
80 KiB
JavaScript
let db = "";
|
||
const { ObjectId } = require("mongodb");
|
||
const dayjs = require("dayjs");
|
||
const { decryptOrderList } = require("../consult-order/format");
|
||
const {
|
||
formatUploadRxData,
|
||
formatOnlineMedicinePurchaseData,
|
||
formatReviewData,
|
||
formatWinePurchaseData,
|
||
encryptDiagnosticRecordFields,
|
||
decryptDiagnosticRecordFields,
|
||
decryptDiagnosticRecordList,
|
||
buildHybridSearchQuery,
|
||
} = require("./format");
|
||
const zytHis = require("../zyt-his");
|
||
const consultOrder = require("../consult-order");
|
||
const tencentIM = require("../tencent-im");
|
||
const beijingCa = require("../beijing-ca");
|
||
const BigNumber = require("bignumber.js");
|
||
const timeApi = require("../hlw-time-duration");
|
||
const types = require("./types");
|
||
const { getConfig } = require("../utils/config");
|
||
|
||
const PrescriptionTypeMap = {
|
||
onlineMedicinePurchase: 'onlineMedicinePurchase',
|
||
onlineConsult: 'storeMedicinePurchase',
|
||
wineConsult: 'medicalWinePurchase'
|
||
}
|
||
|
||
/**
|
||
* 性能优化建议:
|
||
*
|
||
* 1. 数据库索引建议:
|
||
* - diagnostic-record 集合:
|
||
* db.getCollection("diagnostic-record").createIndex({ "status": 1, "createTime": -1 })
|
||
* db.getCollection("diagnostic-record").createIndex({ "orderId": 1 })
|
||
* db.getCollection("diagnostic-record").createIndex({ "doctorCode": 1, "status": 1 })
|
||
* db.getCollection("diagnostic-record").createIndex({ "patientId": 1, "status": 1, "orderSource": 1 })
|
||
*
|
||
* - consult-order 集合:
|
||
* db.getCollection("consult-order").createIndex({ "orderId": 1 })
|
||
* db.getCollection("consult-order").createIndex({ "orderStatus": 1 })
|
||
*
|
||
* 2. 查询优化:
|
||
* - getAuditDiagnosisList 函数已优化为先分页后关联查询,避免大数据量 $lookup
|
||
* - 使用 Promise.all 并行执行不相关的查询操作
|
||
* - 避免在查询条件中使用正则表达式,考虑使用全文检索
|
||
*
|
||
* 3. 数据冗余建议(可选):
|
||
* - 在 diagnostic-record 中冗余 consult-order 的关键字段(如患者姓名、手机号等)
|
||
* - 减少跨集合查询的需求
|
||
*/
|
||
|
||
// 判断当前是否是 zytDev 环境
|
||
function isZytDevEnv() {
|
||
console.log("当前环境变量 CONFIG_NODE_ENV:", process.env.CONFIG_NODE_ENV);
|
||
return (
|
||
process.env.CONFIG_NODE_ENV === "zytDev" ||
|
||
process.env.CONFIG_NODE_ENV === "development"
|
||
);
|
||
}
|
||
|
||
// 根据环境决定是否调用 beijingCa
|
||
async function conditionalCallBeijingCa(params, dbParam) {
|
||
if (isZytDevEnv()) {
|
||
console.log("在 zytDev 环境下,跳过调用 beijingCa");
|
||
return {
|
||
success: true,
|
||
userId: params.code || params.userId || "mock_user_id",
|
||
message: "在 zytDev 环境下模拟返回",
|
||
};
|
||
}
|
||
return await beijingCa(params, dbParam);
|
||
}
|
||
|
||
const status = {
|
||
init: "INIT", //待审核
|
||
pass: "PASS", //审核通过
|
||
unpass: "UNPASS", //审核未通过
|
||
expired: "EXPIRED", //订单过期 无法
|
||
passInvalid: "PASS_INVALID", //通过的处方作废 (目前就订单退费会导致处方作废)
|
||
unpassInvalid: "UNPASS_INVALID", //未通过的处方作废 (目前就订单退费会导致处方作废)
|
||
invalid: "INVALID", //处方作废 (目前就订单退费会导致处方作废)
|
||
};
|
||
|
||
module.exports = async (item, mongodb) => {
|
||
db = mongodb;
|
||
switch (item.type) {
|
||
case "addConsultDiagnosis":
|
||
return await addConsultDiagnosis(item, db);
|
||
case "getOrderDiagnosis":
|
||
return await getOrderDiagnosis(item, db);
|
||
case "getDiagnosisById":
|
||
return await getDiagnosisById(item, db);
|
||
case "getStoreDiagnosisRecord":
|
||
return await getStoreDiagnosisRecord(item, db);
|
||
case "getAuditDiagnosisList":
|
||
return await getAuditDiagnosisList(item, db);
|
||
case "getAuditedDiagnosisList":
|
||
return await getAuditedDiagnosisList(item, db);
|
||
case "getPatientDiagnosisRecord":
|
||
return await getPatientDiagnosisRecord(item, db);
|
||
case "getPatientPassDiagnosisRecord":
|
||
return await getPatientPassDiagnosisRecord(item, db);
|
||
case "auditDiagnosis":
|
||
return await auditDiagnosis(item, db);
|
||
case "uploadDiagnosis":
|
||
return await uploadDiagnosis(item, db);
|
||
case "orderHasDiagnosis":
|
||
return await orderHasDiagnosis(item, db);
|
||
case "getLatestSubmitTime":
|
||
return await getLatestSubmitTime(item, db);
|
||
case "reUploadDiagnosticRecord":
|
||
return await reUploadDiagnosticRecord(item, db);
|
||
case "discardDiagnosticRecord":
|
||
return await discardDiagnosticRecord(item, db);
|
||
case "getAllDiagnosisRecord":
|
||
return await getAllDiagnosisRecord(item, db);
|
||
case "getDoctorRxStats":
|
||
return await getDoctorRxStats(item, db);
|
||
case "getPharmacistRxStats":
|
||
return await getPharmacistRxStats(item, db);
|
||
case "checkRxUploadStatus":
|
||
return await checkRxUploadStatus(item, db);
|
||
case "getAccountRxList":
|
||
return await getAccountRxList(item, db);
|
||
case "getRxDetail":
|
||
return await getRxDetail(item, db);
|
||
case "getAccountHistoryDrugs":
|
||
return await getAccountHistoryDrugs(item, db);
|
||
case "getPatientHistoryDrugs":
|
||
return await getPatientHistoryDrugs(item, db);
|
||
case "getRxMedicineOrderStatus":
|
||
return await getRxMedicineOrderStatus(item, db);
|
||
case "runOnlineRxExpireSchedule":
|
||
return await runOnlineRxExpireSchedule(db);
|
||
case "getPatientHomeRxCount":
|
||
return await getPatientHomeRxCount(item, db);
|
||
case "getReUploadRxList":
|
||
return await getReUploadRxList(item, db);
|
||
case "handleReUploadRx":
|
||
return await handleReUploadRx(item, db);
|
||
case "getPatientWineBuyCount":
|
||
return await getPatientWineBuyCount(item, db);
|
||
case "getYbReviewResult":
|
||
return await getYbReviewResult(item, db);
|
||
}
|
||
};
|
||
|
||
let scheduleTaskIsRunning = false;
|
||
|
||
async function addConsultDiagnosis(item) {
|
||
let { params = {} } = item;
|
||
const corpId = typeof item.corpId === "string" ? item.corpId.trim() : '';
|
||
const { orderId, doctorCode } = params;
|
||
if (
|
||
typeof orderId !== "string" ||
|
||
orderId.trim() === "" ||
|
||
typeof doctorCode !== "string" ||
|
||
doctorCode.trim() === ""
|
||
) {
|
||
return { success: false, message: "参数错误" };
|
||
}
|
||
const timeData = { type: 'addConsultDiagnosis', startTime: dayjs().format('YYYY-MM-DD HH:mm:ss'), orderId }
|
||
try {
|
||
const order = await db.collection("consult-order").findOne(
|
||
{ orderId },
|
||
{
|
||
projection: {
|
||
createTime: 1,
|
||
orderStatus: 1,
|
||
registerId: 1,
|
||
medorg_order_no: 1,
|
||
accountId: 1,
|
||
prescriptionStartTime: 1,
|
||
consultType: 1,
|
||
areaCode: 1,
|
||
pickUpType: 1,
|
||
sex: 1,
|
||
age: 1
|
||
},
|
||
}
|
||
);
|
||
timeData.orderQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||
if (!order) {
|
||
return { success: false, message: "订单不存在" };
|
||
}
|
||
if (order.orderStatus !== "processing") {
|
||
return { success: false, message: "当前订单状态不支持开方" };
|
||
}
|
||
if (!(order.createTime > dayjs().startOf("day").valueOf())) {
|
||
return { success: false, message: "当前订单不在有效期内" };
|
||
}
|
||
timeApi({ type: "addDoctorRxDuration", orderId }, db);
|
||
// 提交诊断前, 先判单CA 自动签名是否失效
|
||
const res = await conditionalCallBeijingCa({
|
||
type: "autoSignVerifyCA",
|
||
code: doctorCode,
|
||
});
|
||
timeData.caSignVerifyTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||
const doctorCAUserId = res.userId;
|
||
if (!res.success) {
|
||
return { success: false, message: "CA自动签名已失效, 请开启自动签" };
|
||
}
|
||
// 判断是否医生发的消息是否大于2条
|
||
const chatCount = await db.collection("im-chat-msg").countDocuments({
|
||
From_Account: doctorCode,
|
||
To_Account: orderId,
|
||
});
|
||
timeData.chatCountQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||
if (chatCount < 3) {
|
||
return {
|
||
success: false,
|
||
message: "请先向患者发送2条提问消息后再开方",
|
||
};
|
||
}
|
||
const record = await db
|
||
.collection("diagnostic-record")
|
||
.findOne(
|
||
{ orderId, doctorCode },
|
||
{ projection: { _id: 1, status: 1, pharmacistNo: 1 } }
|
||
);
|
||
timeData.recordQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||
if (record && record.status === status.init) {
|
||
return { success: false, message: "该订单已存在诊断信息,请勿重复添加" };
|
||
}
|
||
|
||
if (record && record.status === status.pass) {
|
||
return { success: false, message: "该订单已审核通过,请勿重复添加" };
|
||
}
|
||
if (order.consultType === "onlineMedicinePurchase") {
|
||
const { success, message, drugs } = await getMedicinesWithHisChargeInfo(params.drugs, order.areaCode);
|
||
if (!success) {
|
||
return { success, message }
|
||
}
|
||
params.drugs = drugs;
|
||
}
|
||
if (order.consultType === "wineConsult") {
|
||
const { success, message, wines } = await getWinesWithHisChargeInfo(params.wines);
|
||
if (!success) {
|
||
return { success, message }
|
||
}
|
||
params.wines = wines;
|
||
params.pickUpType = order.pickUpType;
|
||
}
|
||
if (record && record.status === status.unpass) {
|
||
return await rewriteConsultDiagnosis(
|
||
params,
|
||
record._id,
|
||
corpId,
|
||
record.pharmacistNo,
|
||
timeData,
|
||
order
|
||
);
|
||
}
|
||
// 获取推荐药师 随机获取药师
|
||
const {
|
||
success,
|
||
message,
|
||
data: pharmacist,
|
||
} = await getRecommendPharmacist({ type: order.consultType === 'wineConsult' ? 'chinese' : 'west' });
|
||
timeData.pharmacistQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||
if (!success) {
|
||
return { success: false, message };
|
||
}
|
||
const common = require('../../common')
|
||
const data = {
|
||
...params,
|
||
createTime: Date.now(),
|
||
status: status.init,
|
||
pharmacistNo: pharmacist.pharmacistNo,
|
||
pharmacist: pharmacist.pharmacistName,
|
||
doctorCAUserId,
|
||
sex: order.sex,
|
||
age: order.age,
|
||
prescriptionType: PrescriptionTypeMap[order.consultType],
|
||
accountId: order.accountId,
|
||
medOrgOrderNo: order.registerId || order.medorg_order_no,
|
||
areaCode: order.areaCode,
|
||
};
|
||
if (order.consultType === 'wineConsult') {
|
||
data.medOrgOrderNo = common.generateUniqueStr('R')
|
||
}
|
||
|
||
// 由后端统一设置处方时间:
|
||
// - 开始时间从 consult-order 读取(接单时已由后端写入)
|
||
// - 结束时间为首次提交医嘱的服务器时间
|
||
const hlwConfig = await db.collection('hlw-config').findOne({ corpId });
|
||
// 首次开方间隔时间(和接诊时间的时间间隔 单位秒)
|
||
const firstSubmitRxIntervel = hlwConfig && Number.isInteger(hlwConfig.firstSubmitRxIntervel) ? hlwConfig.firstSubmitRxIntervel : 0;
|
||
// 医生提交处方时间间隔
|
||
const submitRxInterval = hlwConfig && Number.isInteger(hlwConfig.submitRxInterval) ? hlwConfig.submitRxInterval : 0;
|
||
if (typeof order.prescriptionStartTime !== "undefined") {
|
||
data.prescriptionStartTime = order.prescriptionStartTime;
|
||
}
|
||
data.prescriptionEndTime = Date.now();
|
||
if (firstSubmitRxIntervel > 0 && (data.prescriptionEndTime - data.prescriptionStartTime) <= (firstSubmitRxIntervel * 1000)) {
|
||
return { success: false, message: `接诊后${firstSubmitRxIntervel}秒内不允许提交处方, 请和患者充分沟通后再开方` }
|
||
}
|
||
if (!data.medOrgOrderNo) {
|
||
data.medOrgOrderNo = order.registerId || order.medorg_order_no;
|
||
}
|
||
if (!data.accountId && order.accountId) {
|
||
data.accountId = order.accountId;
|
||
}
|
||
if (submitRxInterval > 0) {
|
||
const lastSubmitRecord = await db.collection('doctor-last-submit-time').findOne({ doctorCode: data.doctorCode });
|
||
const submitTime = lastSubmitRecord && lastSubmitRecord.submitTime > 0 ? lastSubmitRecord.submitTime : 0;
|
||
const diff = Math.ceil((Date.now() - submitTime) / 1000);
|
||
const interval = Math.ceil(submitRxInterval - diff);
|
||
if (interval > 0) {
|
||
return { success: false, message: `提交处方太频繁,请${interval}秒后再试` }
|
||
}
|
||
if (lastSubmitRecord) {
|
||
await db.collection('doctor-last-submit-time').updateOne({ doctorCode: data.doctorCode }, { $set: { submitTime: Date.now() } });
|
||
} else {
|
||
await db.collection('doctor-last-submit-time').insertOne({ doctorCode: data.doctorCode, submitTime: Date.now() });
|
||
}
|
||
}
|
||
if (['onlineMedicinePurchase', 'medicalWinePurchase'].includes(data.prescriptionType)) {
|
||
const { medicinePurchaseRxDuration } = await getConfig(corpId);
|
||
if (Number.isInteger(medicinePurchaseRxDuration) && medicinePurchaseRxDuration >= 10) {
|
||
data.expireTime = dayjs().add(medicinePurchaseRxDuration, 'minute').valueOf();
|
||
} else {
|
||
return { success: false, message: "配药处方持续时长配置不正确, 请检查配置(最小10分钟)" };
|
||
}
|
||
}
|
||
// 加密敏感字段
|
||
const encryptedData = encryptDiagnosticRecordFields(data);
|
||
const { insertedId } = await db
|
||
.collection("diagnostic-record")
|
||
.insertOne(encryptedData);
|
||
timeData.insertRecordTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||
timeData.rxId = insertedId;
|
||
|
||
// 发起咨询后, 咨询订单状态变为已完成
|
||
await updateOrderStatusAndSendNotification({
|
||
orderId,
|
||
corpId,
|
||
doctorCode,
|
||
notification: `处方已开具,审方中.....请稍等`,
|
||
syncOtherMachine: 1,
|
||
orderStatus: "completed",
|
||
msgType: "MEDICALADVICE",
|
||
}, timeData);
|
||
timeData.duration = dayjs().diff(timeData.startTime, 's');
|
||
if (timeData.duration > 3) {
|
||
db.collection("perf-observation").insertOne(timeData);
|
||
}
|
||
return {
|
||
success: true,
|
||
message: "新增成功",
|
||
data: insertedId,
|
||
doctorCAUserId,
|
||
};
|
||
} catch (err) {
|
||
return {
|
||
success: false,
|
||
message: err.message || "新增失败",
|
||
};
|
||
}
|
||
}
|
||
// 未通过的处方 重新提交 原药师在线的话
|
||
async function rewriteConsultDiagnosis(item, _id, corpId, pharmacistNo, timeData, order) {
|
||
const {
|
||
complaint,
|
||
presentIllness,
|
||
dispose,
|
||
diagnosisList,
|
||
drugs,
|
||
orderId,
|
||
doctorCode,
|
||
} = item;
|
||
try {
|
||
timeData.type = 'rewriteConsultDiagnosis';
|
||
const data = {
|
||
complaint,
|
||
presentIllness,
|
||
dispose,
|
||
diagnosisList,
|
||
drugs,
|
||
wines: item.wines,
|
||
updateTime: Date.now(),
|
||
status: status.init,
|
||
};
|
||
const doctorType = order.consultType === 'wineConsult' ? 'chinese' : 'west'
|
||
const {
|
||
success,
|
||
message,
|
||
data: pharmacist,
|
||
} = await getRecommendPharmacist({ pharmacistNo, type: doctorType });
|
||
timeData.pharmacistQueryTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||
if (!success) {
|
||
return { success: false, message };
|
||
}
|
||
// 如果药师不在线, 则重新获取药师 doctorNo doctorName
|
||
if (pharmacist && pharmacist.doctorNo !== pharmacistNo) {
|
||
data.pharmacistNo = pharmacist.doctorNo;
|
||
data.pharmacist = pharmacist.doctorName;
|
||
}
|
||
// 加密敏感字段(如果有)
|
||
const encryptedData = encryptDiagnosticRecordFields(data);
|
||
const { modifiedCount } = await db
|
||
.collection("diagnostic-record")
|
||
.updateOne({ _id }, { $set: encryptedData });
|
||
await updateOrderStatusAndSendNotification({
|
||
orderId,
|
||
corpId,
|
||
doctorCode,
|
||
notification: "医生重新提交诊断, 等待审方中",
|
||
syncOtherMachine: 2,
|
||
orderStatus: "completed",
|
||
msgType: "REPEATMEDICALADVICE",
|
||
}, timeData);
|
||
timeData.rewriteStatus = modifiedCount === 1 ? 'success' : 'fail';
|
||
timeData.duration = dayjs().diff(timeData.startTime, 's');
|
||
if (timeData.duration > 3) {
|
||
db.collection("perf-observation").insertOne(timeData);
|
||
}
|
||
if (modifiedCount === 1) {
|
||
return { success: true, message: "更新成功" };
|
||
}
|
||
return { success: false, message: "更新失败" };
|
||
} catch (e) {
|
||
return { success: false, message: e.message || "更新失败" };
|
||
}
|
||
}
|
||
|
||
async function getDiagnosisById(ctx, db) {
|
||
try {
|
||
const _id = ObjectId.isValid(ctx.id) ? new ObjectId(ctx.id) : null;
|
||
if (!_id) {
|
||
return { success: false, message: "参数错误" };
|
||
}
|
||
const data = await db.collection("diagnostic-record").findOne({ _id });
|
||
if (!data) {
|
||
return { success: false, message: "查询失败" };
|
||
}
|
||
// 解密敏感字段
|
||
const decryptedData = decryptDiagnosticRecordFields(data);
|
||
return { success: true, message: "查询成功", data: decryptedData };
|
||
} catch (e) {
|
||
return { success: false, message: e?.message }
|
||
}
|
||
}
|
||
|
||
async function getOrderDiagnosis(item, db) {
|
||
const { orderId, doctorNo: doctorCode, status } = item;
|
||
if (typeof orderId !== "string" || orderId.trim() === "") {
|
||
return { success: false, message: "参数错误" };
|
||
}
|
||
try {
|
||
// 兼容仅按 orderId 查询的场景(如从购药订单反查处方)
|
||
const query = { orderId };
|
||
if (typeof doctorCode === "string" && doctorCode.trim() !== "") {
|
||
query.doctorCode = doctorCode.trim();
|
||
}
|
||
|
||
if (typeof status === "string" && status.trim() !== "") {
|
||
query.status = status;
|
||
}
|
||
const data = await db.collection("diagnostic-record").findOne(query);
|
||
// 解密敏感字段
|
||
const decryptedData = decryptDiagnosticRecordFields(data);
|
||
return { success: true, message: "查询成功", data: decryptedData };
|
||
} catch (e) {
|
||
return { success: false, message: e.message || "查询失败" };
|
||
}
|
||
}
|
||
|
||
async function getStoreDiagnosisRecord(item, db) {
|
||
if (typeof item.storeId !== "string" || item.storeId.trim() === "") {
|
||
return { success: false, message: "参数错误" };
|
||
}
|
||
const page = item.page > 0 && Number.isInteger(item.page) ? item.page : 1;
|
||
const pageSize =
|
||
item.pageSize > 0 && Number.isInteger(item.pageSize) ? item.pageSize : 20;
|
||
const date =
|
||
item.date && dayjs(item.date).isValid() ? dayjs(item.date) : dayjs();
|
||
const createTime = {
|
||
$gte: date.startOf("day").valueOf(),
|
||
$lte: date.endOf("day").valueOf(),
|
||
};
|
||
const query = {
|
||
drugStoreNo: item.storeId,
|
||
orderSource: "MATEPAD",
|
||
status: status.pass,
|
||
createTime,
|
||
};
|
||
if (typeof item.keyword == "string" && item.keyword.trim() !== "") {
|
||
// 使用混合搜索支持明文和密文数据
|
||
const nameCondition = buildHybridSearchQuery("name", item.keyword, false);
|
||
const orConditions = [
|
||
{ doctorName: { $regex: item.keyword.trim(), $options: "i" } },
|
||
];
|
||
|
||
// 如果构建了name搜索条件,添加到$or中
|
||
if (nameCondition && nameCondition.$or) {
|
||
orConditions.push(...nameCondition.$or);
|
||
} else if (nameCondition) {
|
||
orConditions.push(nameCondition);
|
||
}
|
||
|
||
query.$or = orConditions;
|
||
}
|
||
try {
|
||
const total = await db
|
||
.collection("diagnostic-record")
|
||
.countDocuments(query);
|
||
const list = await db
|
||
.collection("diagnostic-record")
|
||
.find(query)
|
||
.sort({ auditTime: -1 })
|
||
.skip((page - 1) * pageSize)
|
||
.limit(pageSize)
|
||
.toArray();
|
||
// 查询今日的处方 对已上传但未获取 his处方上传状态的处方进行查询
|
||
const start = dayjs().startOf("day").valueOf();
|
||
const toSignList = list
|
||
.filter(
|
||
(i) =>
|
||
i.patientId &&
|
||
i.medOrgOrderNo &&
|
||
i.status === status.pass &&
|
||
i.uploadStatus === "uploaded" &&
|
||
!i.hisUploadStatus &&
|
||
i.createTime > start
|
||
)
|
||
.map((i) => ({
|
||
_id: i._id,
|
||
patientId: i.patientId,
|
||
medOrgOrderNo: i.medOrgOrderNo,
|
||
}));
|
||
if (toSignList.length > 0) {
|
||
const task = toSignList.map((data) =>
|
||
zytHis({
|
||
type: "getHisPrescriptionUploadStatus",
|
||
orderno: data.medOrgOrderNo,
|
||
patientId: data.patientId,
|
||
_id: data._id,
|
||
})
|
||
);
|
||
const res = await Promise.all(task);
|
||
const result = res.reduce(
|
||
(acc, item) => {
|
||
if (item.hisUploadStatus === "uploaded") {
|
||
acc.uploaded.push(item._id);
|
||
} else if (item.hisUploadStatus === "error") {
|
||
acc.uploadError.push(item._id);
|
||
} else if (item.hisUploadStatus === "notUploaded") {
|
||
acc.notUploaded.push(item._id);
|
||
}
|
||
if (item.hisUploadStatus && item.hisUploadStatus !== "notUploaded") {
|
||
acc.map[item.orderno] = item.hisUploadStatus;
|
||
}
|
||
return acc;
|
||
},
|
||
{ uploaded: [], uploadError: [], notUploaded: [], map: {} }
|
||
);
|
||
if (result.uploaded.length > 0) {
|
||
await db
|
||
.collection("diagnostic-record")
|
||
.updateMany(
|
||
{ _id: { $in: result.uploaded } },
|
||
{ $set: { hisUploadStatus: "uploaded" } }
|
||
);
|
||
}
|
||
if (result.uploadError.length > 0) {
|
||
await db
|
||
.collection("diagnostic-record")
|
||
.updateMany(
|
||
{ _id: { $in: result.uploadError } },
|
||
{ $set: { hisUploadStatus: "error" } }
|
||
);
|
||
}
|
||
if (result.notUploaded.length > 0) {
|
||
await db
|
||
.collection("diagnostic-record")
|
||
.updateMany(
|
||
{ _id: { $in: result.notUploaded }, needReUpload: { $exists: false } },
|
||
{ $set: { needReUpload: true } }
|
||
);
|
||
}
|
||
list.forEach((i) => {
|
||
if (result.map[i.orderId]) {
|
||
i.hisUploadStatus = result.map[i.orderId];
|
||
}
|
||
});
|
||
}
|
||
// 解密敏感字段
|
||
const decryptedList = decryptDiagnosticRecordList(list);
|
||
const wineList = decryptedList.filter(i => i.prescriptionType === 'medicalWinePurchase');
|
||
if (wineList.length) {
|
||
const wines = await db.collection('chinese-medicine-order').find({ rpNo: { $in: wineList.map(i => i._id.toString()) } }).toArray();
|
||
for (let item of wineList) {
|
||
if (item.pickUpType === 'selfTake') {
|
||
item.wineStatus = 'completed';
|
||
continue;
|
||
}
|
||
const wine = wines.find(i => i.rpNo === item._id.toString());
|
||
if (wine) {
|
||
item.wineStatus = wine.status;
|
||
continue;
|
||
}
|
||
if (item.expireTime > Date.now()) {
|
||
item.wineStatus = 'unpay';
|
||
} else {
|
||
item.wineStatus = 'expired';
|
||
}
|
||
}
|
||
}
|
||
return {
|
||
success: true,
|
||
message: "查询成功",
|
||
list: decryptedList,
|
||
total,
|
||
paegs: Math.ceil(total / pageSize),
|
||
};
|
||
} catch (e) {
|
||
return { success: false, message: e.message || "查询失败" };
|
||
}
|
||
}
|
||
|
||
async function getAuditDiagnosisList(item, db) {
|
||
const page = item.page > 0 && Number.isInteger(item.page) ? item.page : 1;
|
||
const pageSize =
|
||
item.pageSize > 0 && Number.isInteger(item.pageSize) ? item.pageSize : 1;
|
||
const query = {
|
||
status: status.init,
|
||
pharmacistNo:
|
||
typeof item.pharmacistNo === "string" ? item.pharmacistNo : "",
|
||
};
|
||
|
||
// 使用混合搜索支持明文和密文数据
|
||
const nameCondition = buildHybridSearchQuery("name", item.name, false);
|
||
if (nameCondition) {
|
||
Object.assign(query, nameCondition);
|
||
}
|
||
|
||
const mobileCondition = buildHybridSearchQuery("mobile", item.mobile, false);
|
||
if (mobileCondition) {
|
||
Object.assign(query, mobileCondition);
|
||
}
|
||
|
||
if (Array.isArray(item.doctorCodes) && item.doctorCodes.length > 0) {
|
||
query.doctorCode = { $in: item.doctorCodes };
|
||
}
|
||
try {
|
||
// 并行执行总数查询和最新时间查询
|
||
const [total, res] = await Promise.all([
|
||
db.collection("diagnostic-record").countDocuments(query),
|
||
db.collection("diagnostic-record").findOne(
|
||
{},
|
||
{
|
||
sort: { createTime: -1 },
|
||
projection: { createTime: 1, _id: 0 },
|
||
}
|
||
),
|
||
]);
|
||
|
||
// 先查询分页的 diagnostic-record 数据
|
||
const diagnosticRecords = await db
|
||
.collection("diagnostic-record")
|
||
.find(query)
|
||
.sort({ prescriptionStartTime: 1 })
|
||
.skip((page - 1) * pageSize)
|
||
.limit(pageSize)
|
||
.toArray();
|
||
|
||
if (diagnosticRecords.length === 0) {
|
||
return {
|
||
success: true,
|
||
message: "查询成功",
|
||
list: [],
|
||
total,
|
||
latestTime: res && res.createTime ? res.createTime : 0,
|
||
};
|
||
}
|
||
|
||
// 提取所有 orderId
|
||
const orderIds = diagnosticRecords.map((record) => record.orderId);
|
||
|
||
// 批量查询对应的 consult-order 数据
|
||
const orders = await db
|
||
.collection("consult-order")
|
||
.find({ orderId: { $in: orderIds } })
|
||
.toArray();
|
||
|
||
const decryptedOrders = orders.length > 0 ? decryptOrderList(orders) : orders;
|
||
// 创建 orderId 到 order 的映射
|
||
const orderMap = decryptedOrders.reduce((map, order) => {
|
||
map[order.orderId] = order;
|
||
return map;
|
||
}, {});
|
||
|
||
// 合并数据
|
||
const list = diagnosticRecords.map((record) => ({
|
||
...record,
|
||
order: orderMap[record.orderId] || {},
|
||
}));
|
||
|
||
// 解密敏感字段
|
||
const decryptedList = decryptDiagnosticRecordList(list);
|
||
|
||
return {
|
||
success: true,
|
||
message: "查询成功",
|
||
list: decryptedList,
|
||
total,
|
||
latestTime: res && res.createTime ? res.createTime : 0,
|
||
};
|
||
} catch (e) {
|
||
return { success: false, message: e.message || "查询失败", latestTime: 0 };
|
||
}
|
||
}
|
||
|
||
async function getPatientDiagnosisRecord(item, db) {
|
||
if (!item.patientId) {
|
||
return { success: false, message: "参数错误" };
|
||
}
|
||
const page = item.page > 0 && Number.isInteger(item.page) ? item.page : 1;
|
||
const orderSource = ["ALIPAY_MINI", "MATEPAD"].includes(item.orderSource)
|
||
? item.orderSource
|
||
: "ALIPAY_MINI";
|
||
const pageSize =
|
||
item.pageSize > 0 && Number.isInteger(item.pageSize) ? item.pageSize : 20;
|
||
const query = { patientId: item.patientId, status: status.pass, orderSource };
|
||
if (typeof item.accountId === 'string' && item.accountId.trim()) {
|
||
query.accountId = item.accountId.trim()
|
||
};
|
||
const allQuery = query
|
||
try {
|
||
const total = await db
|
||
.collection("diagnostic-record")
|
||
.countDocuments(allQuery);
|
||
const list = await db
|
||
.collection("diagnostic-record")
|
||
.find(allQuery, { sort: { auditTime: -1 } })
|
||
.skip((page - 1) * pageSize)
|
||
.limit(pageSize)
|
||
.toArray();
|
||
// 查询今日的处方 对已上传但未获取 his处方上传状态的处方进行查询
|
||
const start = dayjs().startOf("day").valueOf();
|
||
const toSignList = list
|
||
.filter(
|
||
(i) =>
|
||
i.patientId &&
|
||
i.medOrgOrderNo &&
|
||
i.status === status.pass &&
|
||
i.uploadStatus === "uploaded" &&
|
||
!i.hisUploadStatus &&
|
||
i.createTime > start
|
||
)
|
||
.map((i) => ({
|
||
_id: i._id,
|
||
patientId: i.patientId,
|
||
medOrgOrderNo: i.medOrgOrderNo,
|
||
}));
|
||
if (toSignList.length > 0) {
|
||
const task = toSignList.map((data) =>
|
||
zytHis({
|
||
type: "getHisPrescriptionUploadStatus",
|
||
orderno: data.medOrgOrderNo,
|
||
patientId: data.patientId,
|
||
_id: data._id,
|
||
})
|
||
);
|
||
const res = await Promise.all(task);
|
||
const result = res.reduce(
|
||
(acc, item) => {
|
||
if (item.hisUploadStatus === "uploaded") {
|
||
acc.uploaded.push(item._id);
|
||
} else if (item.hisUploadStatus === "error") {
|
||
acc.uploadError.push(item._id);
|
||
} else if (item.hisUploadStatus === "notUploaded") {
|
||
acc.notUploaded.push(item._id);
|
||
}
|
||
if (item.hisUploadStatus && item.hisUploadStatus !== "notUploaded") {
|
||
acc.map[item.orderno] = item.hisUploadStatus;
|
||
}
|
||
return acc;
|
||
},
|
||
{ uploaded: [], uploadError: [], notUploaded: [], map: {} }
|
||
);
|
||
if (result.uploaded.length > 0) {
|
||
await db
|
||
.collection("diagnostic-record")
|
||
.updateMany(
|
||
{ _id: { $in: result.uploaded } },
|
||
{ $set: { hisUploadStatus: "uploaded" } }
|
||
);
|
||
}
|
||
if (result.uploadError.length > 0) {
|
||
await db
|
||
.collection("diagnostic-record")
|
||
.updateMany(
|
||
{ _id: { $in: result.uploadError } },
|
||
{ $set: { hisUploadStatus: "error" } }
|
||
);
|
||
}
|
||
if (result.notUploaded.length > 0) {
|
||
await db
|
||
.collection("diagnostic-record")
|
||
.updateMany(
|
||
{ _id: { $in: result.notUploaded }, needReUpload: { $exists: false } },
|
||
{ $set: { needReUpload: true } }
|
||
);
|
||
}
|
||
list.forEach((i) => {
|
||
if (result.map[i.orderId]) {
|
||
i.hisUploadStatus = result.map[i.orderId];
|
||
}
|
||
});
|
||
}
|
||
// 解密敏感字段
|
||
const decryptedList = decryptDiagnosticRecordList(list);
|
||
return {
|
||
success: true,
|
||
message: "查询成功",
|
||
list: decryptedList,
|
||
total,
|
||
paegs: Math.ceil(total / pageSize),
|
||
};
|
||
} catch (e) {
|
||
return { success: false, message: e.message || "查询失败" };
|
||
}
|
||
}
|
||
|
||
async function getPatientPassDiagnosisRecord(ctx, db) {
|
||
const { startDate, endDate, patientId } = ctx;
|
||
if (typeof patientId !== "string" || patientId.trim() === "") {
|
||
return { success: false, message: "参数错误" };
|
||
}
|
||
const startTime =
|
||
startDate && dayjs(startDate).isValid()
|
||
? dayjs(startDate).startOf("day").valueOf()
|
||
: "";
|
||
const endTime =
|
||
endDate && dayjs(endDate).isValid()
|
||
? dayjs(endDate).endOf("day").valueOf()
|
||
: "";
|
||
try {
|
||
const query = { patientId, status: status.pass };
|
||
if (startTime && endTime) {
|
||
query.createTime = { $gte: startTime, $lte: endTime };
|
||
}
|
||
const res = await db
|
||
.collection("diagnostic-record")
|
||
.find(query)
|
||
.sort({ createTime: -1 })
|
||
.toArray();
|
||
// 解密敏感字段
|
||
const decryptedRes = decryptDiagnosticRecordList(res);
|
||
return { success: true, message: "查询成功", data: decryptedRes };
|
||
} catch (e) {
|
||
return { success: false, message: e.message || "查询失败" };
|
||
}
|
||
}
|
||
|
||
async function getAuditedDiagnosisList(item, db) {
|
||
const page = item.page > 0 && Number.isInteger(item.page) ? item.page : 1;
|
||
const pageSize =
|
||
item.pageSize > 0 && Number.isInteger(item.pageSize) ? item.pageSize : 1;
|
||
const query = {
|
||
status: {
|
||
$in: [
|
||
status.pass,
|
||
status.unpass,
|
||
status.expired,
|
||
status.invalid,
|
||
status.passInvalid,
|
||
status.unpassInvalid,
|
||
],
|
||
},
|
||
pharmacistNo:
|
||
typeof item.pharmacistNo === "string" ? item.pharmacistNo : "",
|
||
};
|
||
|
||
// 使用混合搜索支持明文和密文数据
|
||
const nameCondition = buildHybridSearchQuery("name", item.name, false);
|
||
if (nameCondition) {
|
||
Object.assign(query, nameCondition);
|
||
}
|
||
|
||
const mobileCondition = buildHybridSearchQuery("mobile", item.mobile, false);
|
||
if (mobileCondition) {
|
||
Object.assign(query, mobileCondition);
|
||
}
|
||
|
||
if (Array.isArray(item.doctorCodes) && item.doctorCodes.length > 0) {
|
||
query.doctorCode = { $in: item.doctorCodes };
|
||
}
|
||
try {
|
||
const total = await db
|
||
.collection("diagnostic-record")
|
||
.countDocuments(query);
|
||
|
||
// 先查询分页的 diagnostic-record 数据,按审核时间排序
|
||
const diagnosticRecords = await db
|
||
.collection("diagnostic-record")
|
||
.find(query)
|
||
.sort({ auditTime: -1 })
|
||
.skip((page - 1) * pageSize)
|
||
.limit(pageSize)
|
||
.toArray();
|
||
|
||
if (diagnosticRecords.length === 0) {
|
||
return {
|
||
success: true,
|
||
message: "查询成功",
|
||
list: [],
|
||
total,
|
||
};
|
||
}
|
||
|
||
// 提取所有 orderId
|
||
const orderIds = diagnosticRecords.map((record) => record.orderId);
|
||
|
||
// 批量查询对应的 consult-order 数据
|
||
const orders = await db
|
||
.collection("consult-order")
|
||
.find({ orderId: { $in: orderIds } })
|
||
.toArray();
|
||
|
||
const decryptedData = orders ? decryptOrderList(orders) : orders;
|
||
|
||
// 创建 orderId 到 order 的映射
|
||
const orderMap = decryptedData.reduce((map, order) => {
|
||
map[order.orderId] = order;
|
||
return map;
|
||
}, {});
|
||
|
||
// 合并数据
|
||
const list = diagnosticRecords.map((record) => ({
|
||
...record,
|
||
order: orderMap[record.orderId] || {},
|
||
}));
|
||
|
||
// 解密敏感字段
|
||
const decryptedList = decryptDiagnosticRecordList(list);
|
||
|
||
return {
|
||
success: true,
|
||
message: "查询成功",
|
||
list: decryptedList,
|
||
total,
|
||
};
|
||
} catch (e) {
|
||
return { success: false, message: e.message || "查询失败" };
|
||
}
|
||
}
|
||
|
||
async function auditDiagnosis(item, db) {
|
||
const { pharmacistNo, pharmacist } = item;
|
||
const timeData = { type: 'auditDiagnosis', startTime: dayjs().format('YYYY-MM-DD HH:mm:ss') };
|
||
const res = await conditionalCallBeijingCa({
|
||
type: "autoSignVerifyCA",
|
||
code: pharmacistNo,
|
||
}, db);
|
||
timeData.caSignVerifyTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||
if (!res.success) {
|
||
return { success: false, message: "CA自动签名已失效, 请开启自动签" };
|
||
}
|
||
const pharmacistCAUserId = res.userId;
|
||
if (![status.pass, status.unpass].includes(item.status)) {
|
||
return { success: false, message: "状态错误" };
|
||
}
|
||
if (!pharmacistNo || !pharmacist) {
|
||
return { success: false, message: "审方药师信息不能为空" };
|
||
}
|
||
const ids = Array.isArray(item.ids) ? item.ids.filter(i => typeof i === 'string' && i.trim() !== '') : [];
|
||
if (ids.length === 0) {
|
||
return { success: false, message: "id错误" };
|
||
}
|
||
const reasons = Array.isArray(item.reasons)
|
||
? item.reasons.filter((i) => typeof i === "string" && i.trim() !== "")
|
||
: [];
|
||
if (item.status === status.unpass && reasons.length === 0) {
|
||
return { success: false, message: "请填写拒绝原因" };
|
||
}
|
||
try {
|
||
// 1. 根据待审核id 查询待处理的处方记录
|
||
const _rxList = await db.collection("diagnostic-record").find(
|
||
{ _id: { $in: ids.map((id) => new ObjectId(id)) }, status: status.init, pharmacistNo },
|
||
// { projection: { orderId: 1, _id: 1, name: 1, sex: 1, age: 1, drugs: 1, prescriptionType: 1, diagnosisList: 1 } }
|
||
).toArray();
|
||
// 患者姓名 证件号 手机号解密
|
||
const rxList = _rxList.map(i => decryptDiagnosticRecordFields(i));
|
||
// 2. 根据处方记录的orderId 查询对应的咨询记录
|
||
const consultOrderList = await db.collection("consult-order").find(
|
||
{ orderId: { $in: rxList.map((i) => i.orderId) } },
|
||
{ projection: { _id: 1, orderId: 1, orderStatus: 1, medorg_order_no: 1 } }
|
||
).toArray();
|
||
//3. 根据咨询记录的状态 将待处理的处方列表分为三组 passList, unPassList , expireList, failList
|
||
const failList = [];
|
||
const passList = [];
|
||
const unPassList = [];
|
||
const expireList = [];
|
||
const targetList = item.status === status.pass ? passList : unPassList;
|
||
for (let id of ids) {
|
||
const rx = rxList.find(i => i._id.toString() === id);
|
||
if (!rx) {
|
||
failList.push({ id, message: "处方不存在" });
|
||
continue;
|
||
}
|
||
const data = { rx, status: item.status };
|
||
const order = consultOrderList.find(i => i.orderId === rx.orderId && rx.orderId);
|
||
data.order = order;
|
||
if (order && ["pending", "processing", "completed"].includes(order.orderStatus)) {
|
||
targetList.push(data);
|
||
} else if (order) {
|
||
data.status = status.expired;
|
||
expireList.push(data)
|
||
} else {
|
||
failList.push({ id, message: "咨询订单不存在" });
|
||
}
|
||
}
|
||
const statsCount = {
|
||
allCount: targetList.length + expireList.length, // 总待处理条数
|
||
handleCount: 0, // 审核通过或者审核不通过的处理条数
|
||
expireCount: 0 // 已过期处理成功条数
|
||
}
|
||
// 4. 根据分组 分别处理
|
||
if (passList.length) {
|
||
let successRxList = [];
|
||
const uploadResult = await Promise.all(passList.map(i => uploadRx2His(i.rx, i.order)));
|
||
uploadResult.forEach(i => {
|
||
if (i.success) {
|
||
const rx = passList.find(j => j.rx._id.toString() === i.id.toString());
|
||
successRxList.push(rx);
|
||
} else {
|
||
failList.push({ id: i.id, message: i.message });
|
||
}
|
||
})
|
||
|
||
const { modifiedCount } = await db.collection("diagnostic-record").updateMany(
|
||
{ _id: { $in: successRxList.map(i => i.rx._id) } },
|
||
{
|
||
$set: {
|
||
uploadStatus: "uploaded",
|
||
status: status.pass,
|
||
auditTime: Date.now(),
|
||
}
|
||
}
|
||
)
|
||
statsCount.handleCount += modifiedCount;
|
||
// 处方签名
|
||
successRxList.forEach(({ rx }) => sign(rx, pharmacistCAUserId))
|
||
// 发送审核通过消息
|
||
successRxList.forEach(({ rx }) => attempToSendPassMessage(rx, item.corpId))
|
||
}
|
||
|
||
if (unPassList.length) {
|
||
const { modifiedCount } = await db.collection("diagnostic-record").updateMany(
|
||
{ _id: { $in: unPassList.map(i => i.rx._id) }, status: status.init },
|
||
{
|
||
$set: {
|
||
status: item.status,
|
||
auditTime: Date.now(),
|
||
reasons
|
||
}
|
||
}
|
||
)
|
||
statsCount.handleCount += modifiedCount;
|
||
await db.collection("consult-order").updateMany(
|
||
{ orderId: { $in: unPassList.map(i => i.rx.orderId) } },
|
||
{ $set: { orderStatus: "processing" } }
|
||
)
|
||
unPassList.forEach(({ rx }) => attempToSendUnPassMessage(rx, item.corpId, pharmacist, reasons))
|
||
// 记录一下 药师拒绝的操作
|
||
db.collection("pharmacist-reject-record").insertMany(
|
||
unPassList.map((item) => ({
|
||
recordId: item.rx._id.toString(),
|
||
type: "pharmacistReject",
|
||
pharmacistNo,
|
||
pharmacist,
|
||
reasons,
|
||
createTime: Date.now(),
|
||
}))
|
||
)
|
||
}
|
||
|
||
if (expireList.length) {
|
||
const { modifiedCount } = await db.collection("diagnostic-record").updateMany(
|
||
{ _id: { $in: expireList.map(i => i.rx._id) }, status: status.init },
|
||
{ $set: { status: status.expired }, updateTime: Date.now() }
|
||
)
|
||
statsCount.expireCount += modifiedCount;
|
||
}
|
||
|
||
return {
|
||
success: statsCount.handleCount > 0,
|
||
message: `已审核${item.status === status.pass ? "通过" : "不通过"}${statsCount.handleCount}条数据${statsCount.expireCount > 0 ? `, 已过期${statsCount.expireCount}条数据` : ''}`,
|
||
failList
|
||
}
|
||
|
||
} catch (e) {
|
||
return { success: false, message: e.message || "审核失败" };
|
||
}
|
||
}
|
||
|
||
|
||
async function uploadRx2His(rx, order) {
|
||
const hisUploadFn = {
|
||
storeMedicinePurchase: 'hlwuploadprescription',
|
||
onlineMedicinePurchase: 'uploadMedicinePurchaseRx',
|
||
medicalWinePurchase: 'uploadWinePurchaseRx',
|
||
}
|
||
const formatFn = {
|
||
storeMedicinePurchase: formatUploadRxData,
|
||
onlineMedicinePurchase: formatOnlineMedicinePurchaseData,
|
||
medicalWinePurchase: formatWinePurchaseData,
|
||
}
|
||
try {
|
||
const uploadData = formatFn[rx.prescriptionType](rx, order);
|
||
const type = hisUploadFn[rx.prescriptionType];
|
||
const { success, message } = await zytHis({
|
||
type,
|
||
data: uploadData
|
||
})
|
||
return { success, message, id: rx._id }
|
||
} catch (e) {
|
||
return { success: false, message: `[审核处方上传错误]: ${e.message}`, id: rx._id }
|
||
}
|
||
}
|
||
|
||
async function attempToSendPassMessage(rx, corpId) {
|
||
for (let i = 0; i < 10; i++) {
|
||
try {
|
||
const { success, message } = await tencentIM({
|
||
type: "sendSystemNotification",
|
||
corpId,
|
||
formAccount: rx.doctorCode,
|
||
toAccount: rx.orderId,
|
||
SyncOtherMachine: 1,
|
||
msgBody: [
|
||
{
|
||
MsgType: "TIMCustomElem",
|
||
MsgContent: {
|
||
Data: "AUDIPASS",
|
||
Ext: JSON.stringify({
|
||
id: rx._id,
|
||
orderId: rx.orderId,
|
||
name: rx.name,
|
||
expireTime: rx.expireTime ? dayjs(rx.expireTime).format("YYYY-MM-DD HH:mm") : '',
|
||
time: dayjs(rx.createTime).format("YYYY-MM-DD HH:mm"),
|
||
sex: rx.sex,
|
||
age: rx.age,
|
||
prescriptionType: rx.prescriptionType,
|
||
pickUpType: rx.pickUpType,
|
||
disease: rx.diagnosisList.map(d => d.name).join(", "),
|
||
drugs: rx.drugs.map(d => ({
|
||
drugName: d.drugName,
|
||
spec: d.specification,
|
||
quantity: d.quantity,
|
||
unit: d.unit,
|
||
usageName: d.usageName,
|
||
frequencyName: d.frequencyName,
|
||
days: d.days,
|
||
dosage: d.dosage,
|
||
dosage_unit: d.dosage_unit
|
||
})),
|
||
wines: Array.isArray(rx.wines) ? rx.wines.map(w => ({
|
||
name: w.name,
|
||
quantity: w.quantity,
|
||
details: w.details,
|
||
efficacy: w.efficacy,
|
||
})) : []
|
||
})
|
||
},
|
||
},
|
||
],
|
||
})
|
||
if (success) {
|
||
break;
|
||
}
|
||
console.log(`[发送处方审核通过消息失败(第${i + 1}次)]: ${message}`);
|
||
await new Promise(resolve => setTimeout(resolve, 500 + 100 * i));
|
||
} catch (e) {
|
||
console.log(`[发送处方审核通过消息错误(第${i + 1}次)]: ${e.message}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
async function attempToSendUnPassMessage(rx, corpId, pharmacist, reasons) {
|
||
for (let i = 0; i < 10; i++) {
|
||
try {
|
||
const { success, message } = await tencentIM({
|
||
type: "sendSystemNotification",
|
||
corpId,
|
||
formAccount: rx.orderId,
|
||
toAccount: rx.doctorCode,
|
||
SyncOtherMachine: 2,
|
||
msgBody: [
|
||
{
|
||
MsgType: "TIMCustomElem",
|
||
MsgContent: {
|
||
Data: "AUDIFAIL",
|
||
Desc: "notification",
|
||
Ext: `药师${pharmacist}审方不通过,驳回原因:${reasons.join(",")}`,
|
||
},
|
||
},
|
||
],
|
||
})
|
||
if (success) {
|
||
break;
|
||
}
|
||
console.log(`[发送处方审核通过消息失败(第${i + 1}次)]: ${message}`);
|
||
await new Promise(resolve => setTimeout(resolve, 500 + 100 * i));
|
||
} catch (e) {
|
||
console.log(`[发送处方审核通过消息错误(第${i + 1}次)]: ${e.message}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 对医生和药师进行签名
|
||
async function sign(item, pharmacistCAUserId) {
|
||
const { doctorCAUserId, pharmacist } = item;
|
||
console.log("进入签名");
|
||
const oriData = getOriData(item);
|
||
const doctorRes = await conditionalCallBeijingCa(
|
||
{
|
||
type: "autoSign",
|
||
userId: doctorCAUserId,
|
||
oriData,
|
||
},
|
||
db
|
||
);
|
||
const pharmacistRes = await conditionalCallBeijingCa(
|
||
{
|
||
type: "autoSign",
|
||
userId: pharmacistCAUserId,
|
||
oriData: `${oriData} 药师${pharmacist}审方通过`,
|
||
},
|
||
db
|
||
);
|
||
console.log("医生签名结果", doctorRes);
|
||
console.log("药师签名结果", pharmacistRes);
|
||
}
|
||
|
||
function getOriData(data) {
|
||
const doctorName = data.doctorName;
|
||
const patientName = data.name;
|
||
const diagnosis = data.diagnosisList.map((d) => d.name).join(", ");
|
||
const drugs = data.drugs
|
||
.map(
|
||
(d) =>
|
||
`${d.drugName} (${d.dosage}${d.dosage_unit}, ${d.frequencyName}, ${d.days}天)`
|
||
)
|
||
.join(", ");
|
||
return `医生${doctorName}向患者${patientName}开具的诊断是${diagnosis},开具的药物是${drugs}。`;
|
||
}
|
||
|
||
async function uploadDiagnosis(ids) {
|
||
if (!Array.isArray(ids) || ids.length == 0)
|
||
return {
|
||
success: false,
|
||
message: "参数错误",
|
||
};
|
||
try {
|
||
const res = await db
|
||
.collection("diagnostic-record")
|
||
.find({ _id: { $in: ids }, status: status.pass })
|
||
.limit(ids.length)
|
||
.toArray();
|
||
const list = res.map((item) => ({
|
||
payload: formatUploadRxData(item),
|
||
_id: item._id,
|
||
}));
|
||
const res1 = await Promise.all(
|
||
list.map((item) =>
|
||
zytHis({
|
||
type: "hlwuploadprescription",
|
||
payload: item.payload,
|
||
_id: item._id,
|
||
})
|
||
)
|
||
);
|
||
const successIds = res1.filter((i) => i.success).map((i) => i._id);
|
||
await db
|
||
.collection("diagnostic-record")
|
||
.updateMany(
|
||
{ _id: { $in: successIds } },
|
||
{ $set: { uploadStatus: "uploaded" } }
|
||
);
|
||
const successCount = successIds.length;
|
||
return {
|
||
success: true,
|
||
message: `成功上传${successCount}条数据`,
|
||
};
|
||
} catch (err) {
|
||
console.log(err, err.message);
|
||
return {
|
||
success: false,
|
||
message: err.message || "上传失败",
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
*
|
||
* orderStatus completed(已完成) processing(处理中) pending(待处理) cancelled(已取消) finished(已结束)
|
||
* syncOtherMachine 若不希望将消息同步至 From_Account,则 SyncOtherMachine 填写2;若希望将消息同步至 From_Account,则 SyncOtherMachine 填写1。
|
||
* MsgType TIMTextElem(文本消息) TIMLocationElem(位置消息) TIMFaceElem(表情消息) TIMCustomElem(自定义消息) TIMSoundElem(语音消息) TIMImageElem(图像消息) TIMFileElem(文件消息) TIMVideoFileElem(视频消息)
|
||
* MsgContent.Text 文本消息内容
|
||
* MsgContent.Data 自定义消息类型 MEDICALADVICE(医嘱)
|
||
* MsgContent.Desc notification
|
||
*
|
||
*/
|
||
async function updateOrderStatusAndSendNotification(item, timeData) {
|
||
const {
|
||
orderId,
|
||
doctorCode,
|
||
corpId,
|
||
notification,
|
||
syncOtherMachine,
|
||
orderStatus,
|
||
msgType,
|
||
} = item;
|
||
await consultOrder(
|
||
{
|
||
type: "updateConsultOrderStatus",
|
||
orderId,
|
||
orderStatus,
|
||
corpId,
|
||
},
|
||
db
|
||
);
|
||
if (timeData) {
|
||
timeData.updateOrderStatusTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||
}
|
||
await tencentIM({
|
||
type: "sendSystemNotification",
|
||
corpId,
|
||
formAccount: orderId,
|
||
toAccount: doctorCode,
|
||
SyncOtherMachine: syncOtherMachine,
|
||
msgBody: [
|
||
{
|
||
MsgType: "TIMCustomElem",
|
||
MsgContent: {
|
||
Data: msgType,
|
||
Desc: "notification",
|
||
Ext: notification,
|
||
},
|
||
},
|
||
],
|
||
});
|
||
if (timeData) {
|
||
timeData.sendNotificationTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||
}
|
||
}
|
||
|
||
async function getLatestSubmitTime(ctx) {
|
||
try {
|
||
const pharmacistNo = typeof ctx.pharmacistNo === "string" ? ctx.pharmacistNo : "";
|
||
if (!pharmacistNo) {
|
||
return {
|
||
success: true,
|
||
message: "获取成功",
|
||
data: 0,
|
||
};
|
||
}
|
||
const res = await db.collection("diagnostic-record").findOne(
|
||
{ pharmacistNo },
|
||
{
|
||
sort: { createTime: -1 }, // Sort by createTime in descending order
|
||
projection: { createTime: 1, _id: 0 }, // Only return the createTime field, exclude _id
|
||
}
|
||
);
|
||
const reUploadCount = await db.collection('diagnostic-record').countDocuments({
|
||
prescriptionType: 'storeMedicinePurchase',
|
||
pharmacistNo,
|
||
needReUpload: true,
|
||
createTime: { $gt: dayjs().startOf('day').valueOf() }
|
||
});
|
||
return {
|
||
success: true,
|
||
message: "获取成功",
|
||
data: res ? res.createTime : 0,
|
||
reUploadCount
|
||
};
|
||
} catch (e) {
|
||
return { success: false, message: e.message || "获取失败", data: 0 };
|
||
}
|
||
}
|
||
|
||
async function orderHasDiagnosis(item) {
|
||
if (typeof item.patientId !== "string" || typeof item.orderId !== "string") {
|
||
return {
|
||
success: false,
|
||
message: "参数错误",
|
||
};
|
||
}
|
||
try {
|
||
const count = await db.collection("diagnostic-record").countDocuments({
|
||
patientId: item.patientId,
|
||
orderId: item.orderId,
|
||
});
|
||
return { success: true, message: "查询成功", exist: count > 0 };
|
||
} catch (e) {
|
||
return { success: true, message: "查询成功", exist: false };
|
||
}
|
||
}
|
||
|
||
async function reUploadDiagnosticRecord({ _id }) {
|
||
try {
|
||
const data = await db
|
||
.collection("diagnostic-record")
|
||
.findOne({ _id: new ObjectId(_id) });
|
||
if (!data) return { success: false, message: "处方不存在" };
|
||
if (data.prescriptionType !== "storeMedicinePurchase") {
|
||
return { success: false, message: "暂只支持门店处方重新上传" };
|
||
}
|
||
if (data && data.uploadStatus === "uploaded")
|
||
return { success: false, message: "处方已上传" };
|
||
const { uploaded } = await zytHis({
|
||
type: "hlwPrescriptionOrderStatus",
|
||
orderno: data.orderId,
|
||
patientId: data.patientId,
|
||
});
|
||
if (uploaded) {
|
||
await db
|
||
.collection("diagnostic-record")
|
||
.updateOne(
|
||
{ _id: new ObjectId(_id) },
|
||
{ $set: { uploadStatus: "uploaded", hisUploadStatus: "uploaded" } }
|
||
);
|
||
return { success: true, message: "处方上传成功" };
|
||
}
|
||
const payload = formatUploadRxData(data);
|
||
const { success, message } = await zytHis({
|
||
type: "hlwuploadprescription",
|
||
payload,
|
||
_id: data._id,
|
||
});
|
||
if (success) {
|
||
await db
|
||
.collection("diagnostic-record")
|
||
.updateOne(
|
||
{ _id: new ObjectId(_id) },
|
||
{ $set: { uploadStatus: "uploaded" } }
|
||
);
|
||
return { success: true, message: "处方上传成功" };
|
||
}
|
||
return { success: false, message: message || "重新上传失败" };
|
||
} catch (e) {
|
||
return { success: false, message: e.message || "重新上传失败" };
|
||
}
|
||
}
|
||
|
||
// 废弃处方
|
||
async function discardDiagnosticRecord(ctx) {
|
||
const { orderId } = ctx;
|
||
if (typeof orderId !== "string" || orderId.trim() === "")
|
||
return { success: false, message: "参数错误" };
|
||
try {
|
||
const item = await db.collection("diagnostic-record").findOne(
|
||
{
|
||
orderId,
|
||
status: {
|
||
$nin: [status.passInvalid, status.invalid, status.unpassInvalid],
|
||
},
|
||
},
|
||
{ projection: { status: 1, _id: 1 } }
|
||
);
|
||
if (item) {
|
||
const targetStatus =
|
||
item.status === status.pass
|
||
? status.passInvalid
|
||
: item.status === status.unpass
|
||
? status.unpassInvalid
|
||
: status.invalid;
|
||
await db.collection("diagnostic-record").updateOne(
|
||
{ _id: item._id },
|
||
{
|
||
$set: {
|
||
status: targetStatus,
|
||
},
|
||
}
|
||
);
|
||
return { success: true, message: "废弃成功" };
|
||
}
|
||
return { success: false, message: "处方不存在" };
|
||
} catch (e) {
|
||
return { success: false, message: e.message || "废弃失败" };
|
||
}
|
||
}
|
||
|
||
async function getAllDiagnosisRecord(ctx) {
|
||
const { page, pageSize } = ctx;
|
||
const pageNum = page > 0 && Number.isInteger(page) ? page : 1;
|
||
const size = pageSize > 0 && Number.isInteger(pageSize) ? pageSize : 20;
|
||
const insuranceCodes = Array.isArray(ctx.insuranceCodes) ? ctx.insuranceCodes.filter(i => typeof i === 'string' && i.trim()) : [];
|
||
try {
|
||
const query = {};
|
||
// 使用混合搜索支持明文和密文数据
|
||
const nameCondition = buildHybridSearchQuery("name", ctx.name, false);
|
||
if (nameCondition) {
|
||
Object.assign(query, nameCondition);
|
||
}
|
||
|
||
if (Array.isArray(ctx.doctorCodes) && ctx.doctorCodes.length > 0) {
|
||
query.doctorCode = { $in: ctx.doctorCodes };
|
||
}
|
||
if (Array.isArray(ctx.pharmacistNos) && ctx.pharmacistNos.length > 0) {
|
||
query.pharmacistNo = { $in: ctx.pharmacistNos };
|
||
}
|
||
const { startDate, endDate } = ctx;
|
||
if (startDate && dayjs(startDate).isValid()) {
|
||
query.createTime = { $gte: dayjs(startDate).startOf("day").valueOf() };
|
||
}
|
||
if (endDate && dayjs(endDate).isValid()) {
|
||
query.createTime = {
|
||
...(query.createTime || {}),
|
||
$lte: dayjs(endDate).endOf("day").valueOf(),
|
||
};
|
||
}
|
||
const { auditStartDate, auditEndDate } = ctx;
|
||
if (auditStartDate && dayjs(auditStartDate).isValid()) {
|
||
query.auditTime = {
|
||
$gte: dayjs(auditStartDate).startOf("day").valueOf(),
|
||
};
|
||
}
|
||
if (auditEndDate && dayjs(auditEndDate).isValid()) {
|
||
query.auditTime = {
|
||
...(query.auditTime || {}),
|
||
$lte: dayjs(auditEndDate).endOf("day").valueOf(),
|
||
};
|
||
}
|
||
if (Array.isArray(ctx.status)) {
|
||
query.status = { $in: ctx.status };
|
||
}
|
||
if (ctx.preType === 'chinese') {
|
||
query.prescriptionType = 'medicalWinePurchase';
|
||
} else if (ctx.preType === 'west') {
|
||
query.prescriptionType = { $ne: 'medicalWinePurchase' };
|
||
}
|
||
if (insuranceCodes.length) {
|
||
query.drugs = { $elemMatch: { insurance_code: { $in: insuranceCodes } } }
|
||
}
|
||
const total = await db
|
||
.collection("diagnostic-record")
|
||
.countDocuments(query);
|
||
const list = await db.collection("diagnostic-record").aggregate([
|
||
{ $match: query },
|
||
{ $sort: { createTime: -1 } },
|
||
{ $skip: (pageNum - 1) * size },
|
||
{ $limit: size },
|
||
{
|
||
$lookup: {
|
||
from: "consult-order",
|
||
localField: "orderId",
|
||
foreignField: "orderId",
|
||
as: "orderInfo"
|
||
}
|
||
},
|
||
{
|
||
$addFields: {
|
||
sex: { $arrayElemAt: ["$orderInfo.sex", 0] },
|
||
age: { $arrayElemAt: ["$orderInfo.age", 0] }
|
||
}
|
||
},
|
||
{
|
||
$project: {
|
||
orderInfo: 0
|
||
}
|
||
}
|
||
]).toArray()
|
||
// 解密敏感字段
|
||
const decryptedList = decryptDiagnosticRecordList(list);
|
||
const pages = Math.ceil(total / size);
|
||
|
||
// 兼容旧用法(直接返回 list/total),同时为 PC 端提供 data 包装
|
||
return {
|
||
success: true,
|
||
message: "查询成功",
|
||
list: decryptedList,
|
||
total,
|
||
pages,
|
||
page: pageNum,
|
||
pageSize: size,
|
||
data: {
|
||
list: decryptedList,
|
||
total,
|
||
pages,
|
||
page: pageNum,
|
||
pageSize: size,
|
||
},
|
||
};
|
||
} catch (e) {
|
||
return { success: false, message: e.message || "查询失败" };
|
||
}
|
||
}
|
||
|
||
async function getRecommendPharmacist({ pharmacistNo = "", type = 'west' }) {
|
||
try {
|
||
const query = {
|
||
job: "pharmacist",
|
||
onlineStatus: "online",
|
||
serviceStatus: "enabled",
|
||
}
|
||
if (type === 'chinese') {
|
||
query.openChinesePres = true;
|
||
} else {
|
||
query.openWestPres = true
|
||
}
|
||
const nos = await db
|
||
.collection("hlw-doctor")
|
||
.find(
|
||
query,
|
||
{ projection: { doctorNo: 1, doctorName: 1 } }
|
||
)
|
||
.toArray();
|
||
const pharmacistNos = nos.map((item) => item.doctorNo);
|
||
const matchPharmacist = nos.find(
|
||
(item) => pharmacistNo && item.doctorNo === pharmacistNo
|
||
);
|
||
if (matchPharmacist) {
|
||
return {
|
||
success: true,
|
||
message: "获取推荐药师成功",
|
||
data: matchPharmacist,
|
||
};
|
||
}
|
||
if (nos.length === 0) {
|
||
return { success: false, message: "当前没有药师在线,请稍后处理" };
|
||
}
|
||
|
||
const res = await getTodayPharmacistCount({ pharmacistNos }, db);
|
||
if (!res.success) {
|
||
return res;
|
||
}
|
||
const {
|
||
data: [countMap],
|
||
} = res;
|
||
const auditCount =
|
||
countMap && Array.isArray(countMap.auditCount) ? countMap.auditCount : [];
|
||
const auditedCount =
|
||
countMap && Array.isArray(countMap.auditedCount)
|
||
? countMap.auditedCount
|
||
: [];
|
||
const list = nos
|
||
.map((item) => {
|
||
const audit = auditCount.find((i) => i._id === item.doctorNo);
|
||
const audited = auditedCount.find((i) => i._id === item.doctorNo);
|
||
const auditNum = audit ? audit.count : 0;
|
||
const auditedNum = audited ? audited.count : 0;
|
||
return {
|
||
pharmacistNo: item.doctorNo,
|
||
doctorNo: item.doctorNo,
|
||
pharmacistName: item.doctorName,
|
||
doctorName: item.doctorName,
|
||
weight: new BigNumber(auditNum)
|
||
.plus(new BigNumber(auditedNum).dividedBy(1000000)) //权重计算 已经审核过的权重占比小一点
|
||
.toNumber(),
|
||
};
|
||
})
|
||
.sort((a, b) => a.weight - b.weight); // 按照权重升序排序
|
||
const [{ weight }] = list;
|
||
const lightList = list.filter((i) => i.weight === weight); // 取出权重最小的药师
|
||
const randomIndex = Math.floor(Math.random() * lightList.length); // 随机选取一个药师
|
||
const pharmacist = lightList[randomIndex];
|
||
return { success: true, message: "获取推荐药师成功", data: pharmacist };
|
||
} catch (e) {
|
||
return {
|
||
success: false,
|
||
message: e.message || "获取推荐药师失败",
|
||
};
|
||
}
|
||
}
|
||
|
||
async function getTodayPharmacistCount(ctx) {
|
||
const pharmacistNos = Array.isArray(ctx.pharmacistNos)
|
||
? ctx.pharmacistNos.filter((i) => typeof i === "string")
|
||
: [ctx.pharmacistNos];
|
||
try {
|
||
const res = await db
|
||
.collection("diagnostic-record")
|
||
.aggregate([
|
||
{
|
||
$match: {
|
||
pharmacistNo: { $in: pharmacistNos },
|
||
createTime: {
|
||
$gt: dayjs().startOf("day").valueOf(),
|
||
$lte: dayjs().endOf("day").valueOf(),
|
||
},
|
||
},
|
||
},
|
||
{
|
||
$facet: {
|
||
auditCount: [
|
||
{ $match: { status: status.init } },
|
||
{ $group: { _id: "$pharmacistNo", count: { $sum: 1 } } },
|
||
],
|
||
auditedCount: [
|
||
{ $match: { status: { $in: [status.pass, status.unpass] } } },
|
||
{ $group: { _id: "$pharmacistNo", count: { $sum: 1 } } },
|
||
],
|
||
},
|
||
},
|
||
])
|
||
.toArray();
|
||
return { success: true, message: "查询成功", data: res };
|
||
} catch (e) {
|
||
return { success: false, message: e.message || "查询失败" };
|
||
}
|
||
}
|
||
|
||
async function getDoctorRxStats(ctx) {
|
||
const { doctorCodes, startDate, endDate } = ctx;
|
||
const query = {};
|
||
if (Array.isArray(doctorCodes)) {
|
||
query.doctorCode = { $in: doctorCodes };
|
||
}
|
||
if (startDate && dayjs(startDate).isValid()) {
|
||
query.createTime = { $gte: dayjs(startDate).startOf("day").valueOf() };
|
||
}
|
||
if (endDate && dayjs(endDate).isValid()) {
|
||
query.createTime = {
|
||
...(query.createTime || {}),
|
||
$lte: dayjs(endDate).endOf("day").valueOf(),
|
||
};
|
||
}
|
||
if (typeof ctx.orderSource === "string" && ctx.orderSource.trim() !== "") {
|
||
query.orderSource = ctx.orderSource;
|
||
}
|
||
console.log("查询医生处方统计", query);
|
||
try {
|
||
const res = await db
|
||
.collection("diagnostic-record")
|
||
.aggregate(
|
||
[
|
||
{ $match: query },
|
||
{
|
||
$group: {
|
||
_id: "$doctorCode",
|
||
doctorName: { $first: "$doctorName" },
|
||
count: { $sum: 1 },
|
||
passCount: {
|
||
$sum: { $cond: [{ $eq: ["$status", status.pass] }, 1, 0] },
|
||
},
|
||
passInvalidCount: {
|
||
$sum: {
|
||
$cond: [{ $eq: ["$status", status.passInvalid] }, 1, 0],
|
||
},
|
||
},
|
||
},
|
||
},
|
||
],
|
||
{ allowDiskUse: true }
|
||
)
|
||
.toArray();
|
||
return { success: true, message: "查询成功", data: res };
|
||
} catch (e) {
|
||
return { success: false, message: e.message || "查询失败" };
|
||
}
|
||
}
|
||
|
||
async function getPharmacistRxStats(ctx) {
|
||
const { pharmacistNos, startDate, endDate } = ctx;
|
||
const query = {};
|
||
if (Array.isArray(pharmacistNos)) {
|
||
query.pharmacistNo = { $in: pharmacistNos };
|
||
}
|
||
if (startDate && dayjs(startDate).isValid()) {
|
||
query.createTime = { $gte: dayjs(startDate).startOf("day").valueOf() };
|
||
}
|
||
if (endDate && dayjs(endDate).isValid()) {
|
||
query.createTime = {
|
||
...(query.createTime || {}),
|
||
$lte: dayjs(endDate).endOf("day").valueOf(),
|
||
};
|
||
}
|
||
try {
|
||
const res = await db
|
||
.collection("diagnostic-record")
|
||
.aggregate([
|
||
{ $match: query },
|
||
{
|
||
$group: {
|
||
_id: "$pharmacistNo",
|
||
doctorName: { $first: "$pharmacist" },
|
||
count: { $sum: 1 },
|
||
auditCount: {
|
||
$sum: {
|
||
$cond: [
|
||
{
|
||
$in: [
|
||
"$status",
|
||
[status.pass, status.invalid, status.passInvalid],
|
||
],
|
||
},
|
||
1,
|
||
0,
|
||
],
|
||
},
|
||
},
|
||
expiredCount: {
|
||
$sum: { $cond: [{ $eq: ["$status", status.expired] }, 1, 0] },
|
||
},
|
||
},
|
||
},
|
||
])
|
||
.toArray();
|
||
const rejectRes = await db
|
||
.collection("pharmacist-reject-record")
|
||
.aggregate([
|
||
{ $match: query },
|
||
{
|
||
$group: {
|
||
_id: "$pharmacistNo",
|
||
doctorName: { $first: "$pharmacist" },
|
||
rejectCount: { $sum: 1 },
|
||
},
|
||
},
|
||
])
|
||
.toArray();
|
||
return { success: true, message: "查询成功", data: res, rejectRes };
|
||
} catch (e) {
|
||
return { success: false, message: e.message || "查询失败" };
|
||
}
|
||
}
|
||
|
||
async function checkRxUploadStatus(ctx) {
|
||
const { orderId, registerId, patientId } = ctx;
|
||
if (typeof orderId !== "string" || orderId.trim() === "") {
|
||
return { success: false, message: "订单号不能为空" };
|
||
}
|
||
if (typeof registerId !== "string" || registerId.trim() === "") {
|
||
return { success: false, message: "挂号号不能为空" };
|
||
}
|
||
if (typeof patientId !== "string" || patientId.trim() === "") {
|
||
return { success: false, message: "患者id不能为空" };
|
||
}
|
||
try {
|
||
const rx = await db.collection("diagnostic-record").findOne({
|
||
orderId,
|
||
patientId
|
||
});
|
||
if (!rx) {
|
||
return { success: false, message: "处方不存在" };
|
||
}
|
||
const { status } = await zytHis({
|
||
type: "hlwPrescriptionOrderStatus",
|
||
orderno: rx.medOrgOrderNo,
|
||
patientId,
|
||
});
|
||
if (status === "0") {
|
||
return {
|
||
success: true,
|
||
message: "正在获取订单信息,请等待1分钟后再发起退费申请!",
|
||
};
|
||
} else if (["1", "2", "3"].includes(status)) {
|
||
return { success: true, message: "处方已上传或者已经作废" };
|
||
}
|
||
return { success: false, message: "处方上传状态未知,请稍后再试" };
|
||
} catch (e) {
|
||
return { success: false, message: e.message || "查询失败" };
|
||
}
|
||
}
|
||
|
||
async function getAccountRxList(ctx) {
|
||
try {
|
||
if (typeof ctx.accountId !== 'string' || ctx.accountId.trim() === '') {
|
||
return { success: false, message: "账号id不能为空" };
|
||
}
|
||
const page = Number.isInteger(ctx.page) && ctx.page > 0 ? ctx.page : 1;
|
||
const pageSize = Number.isInteger(ctx.pageSize) && ctx.pageSize > 0 ? ctx.pageSize : 10;
|
||
const query = { orderSource: "ALIPAY_MINI", accountId: ctx.accountId.trim(), status: status.pass };
|
||
const [list, total] = await Promise.all([
|
||
db.collection("diagnostic-record").find(query, { projection: types.output.accountRxList }).sort({ createTime: -1 }).skip((page - 1) * pageSize).limit(pageSize).toArray(),
|
||
db.collection("diagnostic-record").countDocuments(query),
|
||
]);
|
||
await getHisPrescriptionUploadStatus(list);
|
||
// 获取处方单号
|
||
const rpNos = list.filter(i => i.prescriptionType === 'onlineMedicinePurchase').map(i => i._id.toString());
|
||
const orderList = await db.collection("medicine-purchase-order").find({ rpNo: { $in: rpNos } }, { projection: types.output.medicinePurchaseOrder }).toArray();
|
||
return { success: true, message: "查询成功", orderList, list: decryptDiagnosticRecordList(list), total, pages: Math.ceil(total / pageSize), now: Date.now() };
|
||
} catch (e) {
|
||
return { success: false, message: `获取处方记录失败:${e?.message || '未知错误 '}` };
|
||
}
|
||
}
|
||
|
||
async function getRxDetail(ctx) {
|
||
try {
|
||
if (typeof ctx.id !== 'string' || ctx.id.trim() === '') {
|
||
return { success: false, message: "处方id不能为空" };
|
||
}
|
||
const query = {};
|
||
query._id = new ObjectId(ctx.id.trim());
|
||
if (typeof ctx.accountId === 'string' && ctx.accountId.trim() !== '') {
|
||
query.accountId = ctx.accountId.trim();
|
||
}
|
||
const record = await db.collection("diagnostic-record").findOne(query);
|
||
if (!record) {
|
||
return { success: false, message: "处方不存在" };
|
||
}
|
||
if (record.prescriptionType === 'onlineMedicinePurchase') {
|
||
const order = await db.collection("medicine-purchase-order").findOne({ rpNo: record._id.toString() }, { projection: types.output.medicinePurchaseOrder });
|
||
if (order) {
|
||
record.purchaseOrder = order;
|
||
}
|
||
}
|
||
return { success: true, message: "查询成功", data: decryptDiagnosticRecordFields(record), now: Date.now() };
|
||
} catch (e) {
|
||
return { success: false, message: `获取处方详情失败:${e?.message || '未知错误 '}` };
|
||
}
|
||
}
|
||
|
||
async function getHisPrescriptionUploadStatus(list) {
|
||
try {
|
||
const toSignList = list.filter(i => i.prescriptionType === 'storeMedicinePurchase' && i.medOrgOrderNo && i.hisUploadStatus === 'uploaded' && !i.hisUploadStatus && i.createTime > dayjs().startOf("day").valueOf()).map(i => ({
|
||
_id: i._id,
|
||
patientId: i.patientId,
|
||
medOrgOrderNo: i.medOrgOrderNo,
|
||
}));
|
||
if (toSignList.length > 0) {
|
||
const task = toSignList.map((data) =>
|
||
zytHis({
|
||
type: "getHisPrescriptionUploadStatus",
|
||
orderno: data.medOrgOrderNo,
|
||
patientId: data.patientId,
|
||
_id: data._id,
|
||
})
|
||
);
|
||
const res = await Promise.all(task);
|
||
const result = res.reduce(
|
||
(acc, item) => {
|
||
if (item.hisUploadStatus === "uploaded") {
|
||
acc.uploaded.push(item._id);
|
||
} else if (item.hisUploadStatus === "error") {
|
||
acc.uploadError.push(item._id);
|
||
} else if (item.hisUploadStatus === "notUploaded") {
|
||
acc.notUploaded.push(item._id);
|
||
}
|
||
if (item.hisUploadStatus && item.hisUploadStatus !== "notUploaded") {
|
||
acc.map[item.orderno] = item.hisUploadStatus;
|
||
}
|
||
return acc;
|
||
},
|
||
{ uploaded: [], uploadError: [], notUploaded: [], map: {} }
|
||
);
|
||
if (result.uploaded.length > 0) {
|
||
db.collection("diagnostic-record")
|
||
.updateMany(
|
||
{ _id: { $in: result.uploaded } },
|
||
{ $set: { hisUploadStatus: "uploaded" } }
|
||
);
|
||
}
|
||
if (result.uploadError.length > 0) {
|
||
db.collection("diagnostic-record")
|
||
.updateMany(
|
||
{ _id: { $in: result.uploadError } },
|
||
{ $set: { hisUploadStatus: "error" } }
|
||
);
|
||
}
|
||
if (result.notUploaded.length > 0) {
|
||
await db
|
||
.collection("diagnostic-record")
|
||
.updateMany(
|
||
{ _id: { $in: result.notUploaded }, needReUpload: { $exists: false } },
|
||
{ $set: { needReUpload: true } }
|
||
);
|
||
}
|
||
list.forEach((i) => {
|
||
if (result.map[i.orderId]) {
|
||
i.hisUploadStatus = result.map[i.orderId];
|
||
}
|
||
});
|
||
}
|
||
} catch (e) {
|
||
console.log('查询his上传信息错误: ', e.message)
|
||
}
|
||
}
|
||
|
||
async function getAccountHistoryDrugs(ctx) {
|
||
try {
|
||
const patientId = typeof ctx.patientId === 'string' && ctx.patientId.trim() !== '' ? ctx.patientId.trim() : '';
|
||
const accountId = typeof ctx.accountId === 'string' && ctx.accountId.trim() !== '' ? ctx.accountId.trim() : '';
|
||
if (accountId.value == '') {
|
||
return { success: false, message: "账号id不能为空" };
|
||
}
|
||
const query = { accountId, status: status.pass };
|
||
if (patientId) {
|
||
query.patientId = patientId;
|
||
}
|
||
const page = Number.isInteger(ctx.page) && ctx.page > 0 ? ctx.page : 1;
|
||
const pageSize = Number.isInteger(ctx.pageSize) && ctx.pageSize > 0 ? ctx.pageSize : 10;
|
||
const [list, total] = await Promise.all([
|
||
db.collection("diagnostic-record").find(query, { projection: types.output.accountHistoryDrugs }).sort({ createTime: -1 }).skip((page - 1) * pageSize).limit(pageSize).toArray(),
|
||
db.collection("diagnostic-record").countDocuments(query),
|
||
]);
|
||
return { success: true, message: "查询成功", data: list, total, pages: Math.ceil(total / pageSize) };
|
||
} catch (e) {
|
||
return { success: false, message: `获取账号历史药品错误:${e?.message || '未知错误 '}` };
|
||
}
|
||
}
|
||
|
||
async function getPatientHistoryDrugs(ctx) {
|
||
try {
|
||
const patientId = typeof ctx.patientId === 'string' && ctx.patientId.trim() !== '' ? ctx.patientId.trim() : '';
|
||
if (patientId.value == '') {
|
||
return { success: false, message: "患者id不能为空" };
|
||
}
|
||
const query = { patientId, status: status.pass, prescriptionType: { $ne: 'medicalWinePurchase' } };
|
||
const page = Number.isInteger(ctx.page) && ctx.page > 0 ? ctx.page : 1;
|
||
const pageSize = Number.isInteger(ctx.pageSize) && ctx.pageSize > 0 ? ctx.pageSize : 10;
|
||
const [list, total] = await Promise.all([
|
||
db.collection("diagnostic-record").find(query, { projection: types.output.accountHistoryDrugs }).sort({ createTime: -1 }).skip((page - 1) * pageSize).limit(pageSize).toArray(),
|
||
db.collection("diagnostic-record").countDocuments(query),
|
||
]);
|
||
return { success: true, message: "查询成功", data: list, total, pages: Math.ceil(total / pageSize) };
|
||
} catch (e) {
|
||
return { success: false, message: `获取患者历史药品错误:${e?.message || '未知错误 '}` };
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取药品的his收费信息(药品列表中添加his收费信息)
|
||
* @param {*} medicines 药品列表
|
||
* @param {*} areaCode 医保区域代码
|
||
* @returns
|
||
*/
|
||
async function getMedicinesWithHisChargeInfo(medicines, areaCode) {
|
||
try {
|
||
if (!Array.isArray(medicines) || medicines.length === 0) {
|
||
return { success: false, message: "药品列表不能为空" };
|
||
}
|
||
if (typeof areaCode !== 'string' || areaCode.trim() === '') {
|
||
return { success: false, message: "区域代码不能为空" };
|
||
}
|
||
const insuranceCodes = medicines.map(i => i.insurance_code);
|
||
const { success, message, data } = await zytHis({ type: 'getHisDrugInfo', insuranceCodes, areaCode });
|
||
if (!success) {
|
||
return { success, message };
|
||
}
|
||
const hisDrugs = data && Array.isArray(data) ? data : [];
|
||
const drugs = [];
|
||
for (const med of medicines) {
|
||
const hisDrug = hisDrugs.find(j => med.insurance_code && med.insurance_code === j.gjbm);
|
||
if (!hisDrug) {
|
||
return { success: false, messsage: `药品【${med.drugName}】医保编码在his中不存在` };
|
||
}
|
||
const quantity = Number.isInteger(med.quantity) && med.quantity > 0 ? med.quantity : 0;
|
||
if (quantity === 0) {
|
||
return { success: false, messsage: `药品【${med.drugName}】数量不是正整数` };
|
||
}
|
||
const pack_retprice = Number(hisDrug.pack_retprice) == hisDrug.pack_retprice && hisDrug.pack_retprice > 0 && hisDrug.pack_retprice !== '' ? Number(hisDrug.pack_retprice) : 0;
|
||
if (pack_retprice === 0 || !Number.isInteger(pack_retprice * 100)) {
|
||
return { success: false, messsage: `药品【${med.drugName}】价格无效(${pack_retprice})` };
|
||
}
|
||
const totalPrice = new BigNumber(pack_retprice).multipliedBy(quantity).toNumber();
|
||
drugs.push({
|
||
...med,
|
||
hisChargeInfo: {
|
||
drugGroupNo: hisDrug.group_no,
|
||
drugId: hisDrug.charge_code,
|
||
drugSerial: hisDrug.serial,
|
||
drugPrice: hisDrug.pack_retprice,
|
||
drugSumamt: totalPrice
|
||
}
|
||
})
|
||
}
|
||
return { success: true, drugs };
|
||
|
||
} catch (e) {
|
||
return { success: false, message: `获取his药品信息错误: ${e.message}` }
|
||
}
|
||
}
|
||
|
||
async function getWinesWithHisChargeInfo(data) {
|
||
if (!Array.isArray(data) || data.length === 0) {
|
||
return { success: false, message: "浸酒信息错误" };
|
||
}
|
||
const wineIds = data.map(i => new ObjectId(i._id));
|
||
const docs = await db.collection('chinese-medicine').find({ _id: { $in: wineIds } }).toArray();
|
||
const result = [];
|
||
for (let i of data) {
|
||
const wine = docs.find(item => i._id === item._id.toString());
|
||
if (!wine) {
|
||
return { success: false, message: `未查询到浸酒【${i.name}】信息` };
|
||
}
|
||
const quantity = Number.isInteger(i.quantity) && i.quantity > 0 ? i.quantity : 0;
|
||
if (quantity === 0) {
|
||
return { success: false, message: `浸酒【${i.name}】数量错误` };
|
||
}
|
||
result.push({
|
||
_id: wine._id.toString(),
|
||
name: wine.name,
|
||
details: wine.details,
|
||
efficacy: wine.efficacy,
|
||
hisId: wine.hisId,
|
||
unit: wine.unit,
|
||
quantity: i.quantity,
|
||
})
|
||
}
|
||
return { success: true, message: '获取浸酒价格信息成功', wines: result }
|
||
}
|
||
|
||
async function getRxMedicineOrderStatus(ctx, db) {
|
||
try {
|
||
const id = typeof ctx.id === 'string' && ctx.id.trim() !== '' ? ctx.id.trim() : '';
|
||
const accountId = typeof ctx.accountId === 'string' && ctx.accountId.trim() !== '' ? ctx.accountId.trim() : '';
|
||
if (!id || !accountId) {
|
||
return { success: false, message: "处方id或账号id不能为空" };
|
||
}
|
||
const rx = await db.collection('diagnostic-record').findOne({ _id: new ObjectId(id), accountId, prescriptionType: 'onlineMedicinePurchase' }, { projection: { expireTime: 1 } });
|
||
if (!rx) {
|
||
return { success: false, message: "处方不存在" };
|
||
}
|
||
const expired = Date.now() > rx.expireTime;
|
||
const order = await db.collection('medicine-purchase-order').findOne({ rpNo: id, accountId }, { projection: { status: 1 } });
|
||
return { success: true, message: "查询成功", data: { id: rx._id, expired, orderStatus: order ? order.status : 'none' } };
|
||
} catch (e) {
|
||
return { success: false, message: `获取账号购药处方记录失败: ${e.message}` }
|
||
}
|
||
}
|
||
|
||
async function getYbReviewResult(ctx) {
|
||
let { params = {} } = ctx;
|
||
try {
|
||
const drugs = Array.isArray(params.drugs) ? params.drugs : [];
|
||
const insuranceCodes = drugs.map(i => i.insurance_code).filter(Boolean);
|
||
if (insuranceCodes.length === 0) {
|
||
return { success: false, message: "药品列表不能为空" };
|
||
}
|
||
const { data: hisDrugs } = await zytHis({ type: 'getHisDrugInfo', insuranceCodes, ignoreAreaCode: true });
|
||
const hisDrugList = Array.isArray(hisDrugs) ? hisDrugs : [];
|
||
const validDrugs = []
|
||
for (let i of params.drugs) {
|
||
const hisDrug = hisDrugList.find(j => i.insurance_code && i.insurance_code === j.gjbm);
|
||
if (!hisDrug) {
|
||
continue;
|
||
}
|
||
validDrugs.push({
|
||
...i,
|
||
hisChargeInfo: {
|
||
drugGroupNo: hisDrug.group_no,
|
||
drugId: hisDrug.charge_code,
|
||
drugSerial: hisDrug.serial,
|
||
drugPrice: hisDrug.pack_retprice,
|
||
drugSumamt: new BigNumber(hisDrug.pack_retprice).multipliedBy(i.quantity).toNumber()
|
||
}
|
||
})
|
||
}
|
||
if (validDrugs.length === 0) {
|
||
return { success: true, message: '未能有效查询: 药品信息未找到 ', advices: [] }
|
||
}
|
||
params.drugs = validDrugs;
|
||
const payload = formatReviewData(params)
|
||
const { success, advices, message } = await zytHis({
|
||
type: 'ybReviewPrescription',
|
||
data: payload
|
||
})
|
||
return { success, message, advices }
|
||
} catch (e) {
|
||
return { success: false, message: `获取处方审核结果失败: ${e.message}` }
|
||
}
|
||
|
||
}
|
||
|
||
async function runOnlineRxExpireSchedule(db) {
|
||
if (scheduleTaskIsRunning) return;
|
||
scheduleTaskIsRunning = true;
|
||
try {
|
||
const rxList = await db.collection('diagnostic-record').find(
|
||
{
|
||
$or: [
|
||
{ // 在线购药处方过期
|
||
prescriptionType: 'onlineMedicinePurchase',// { $in: ['onlineMedicinePurchase', 'medicalWinePurchase'] },
|
||
status: 'PASS',
|
||
expireTime: { $lt: Date.now() },
|
||
medOrgOrderNo: { $exists: true },
|
||
hisChargeStatus: { $exists: false },
|
||
createTime: { $gt: dayjs().startOf('day').valueOf() }
|
||
},
|
||
{ // 快递的浸酒处方过期
|
||
prescriptionType: 'medicalWinePurchase',
|
||
status: 'PASS',
|
||
pickUpType: 'delivery',
|
||
expireTime: { $lt: Date.now() },
|
||
medOrgOrderNo: { $exists: true },
|
||
hisChargeStatus: { $exists: false },
|
||
createTime: { $gt: dayjs().startOf('day').valueOf() }
|
||
},
|
||
]
|
||
},
|
||
{ projection: { _id: 1, medOrgOrderNo: 1, patientId: 1 } }
|
||
).toArray();
|
||
for (let rx of rxList) {
|
||
const { data } = await zytHis({ type: 'getHisMedicinePurchase', patientId: rx.patientId, orderNo: rx.medOrgOrderNo });
|
||
if (data && data.pay_mark === '5') {
|
||
const { success } = await zytHis({ type: 'cancelMedicinePurchase', patientId: rx.patientId, orderNo: rx.medOrgOrderNo });
|
||
if (success) {
|
||
await db.collection('diagnostic-record').updateOne({ _id: rx._id }, { $set: { hisChargeStatus: 'x' } });
|
||
}
|
||
} else if (data && data.pay_mark) {
|
||
await db.collection('diagnostic-record').updateOne({ _id: rx._id }, { $set: { hisChargeStatus: data.pay_mark } });
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.log(`[处方过期定时任务]: 执行失败: ${e?.message}`)
|
||
return { success: false, message: `执行定时任务出现错误: ${e.message}` }
|
||
}
|
||
scheduleTaskIsRunning = false;
|
||
}
|
||
|
||
async function getPatientHomeRxCount(ctx) {
|
||
try {
|
||
const accountId = typeof ctx.accountId === 'string' && ctx.accountId.trim() !== '' ? ctx.accountId.trim() : '';
|
||
if (!accountId) {
|
||
return { success: false, message: "账号id不能为空" };
|
||
}
|
||
// 使用聚合查询实现待支付处方数量统计
|
||
const result = await db.collection("diagnostic-record").aggregate([
|
||
{
|
||
// 第一步:匹配符合条件的处方
|
||
$match: {
|
||
accountId: accountId,
|
||
status: status.pass,
|
||
prescriptionType: "onlineMedicinePurchase",
|
||
expireTime: { $gt: Date.now() }
|
||
}
|
||
},
|
||
{
|
||
// 第二步:联表查询对应的购药订单
|
||
$lookup: {
|
||
from: "medicine-purchase-order",
|
||
let: {
|
||
rpNo: { $toString: "$_id" },
|
||
accountId: "$accountId"
|
||
},
|
||
pipeline: [
|
||
{
|
||
$match: {
|
||
$expr: {
|
||
$and: [
|
||
{ $eq: ["$disabled", false] },
|
||
{ $eq: ["$rpNo", "$$rpNo"] },
|
||
{ $eq: ["$accountId", "$$accountId"] }
|
||
]
|
||
}
|
||
}
|
||
},
|
||
{
|
||
$project: {
|
||
_id: 1,
|
||
status: 1,
|
||
disabled: 1
|
||
}
|
||
}
|
||
],
|
||
as: "purchaseOrder"
|
||
}
|
||
},
|
||
{
|
||
// 第三步:筛选待支付处方(购药订单不存在或status为unpay)
|
||
$match: {
|
||
$or: [
|
||
{ purchaseOrder: { $size: 0 } }, // 购药订单不存在
|
||
{
|
||
$expr: {
|
||
$eq: [{ $arrayElemAt: ["$purchaseOrder.status", 0] }, "unpay"]
|
||
}
|
||
} // 购药订单状态为unpay(取数组第一个元素的status)
|
||
]
|
||
}
|
||
},
|
||
{
|
||
// 第四步:统计数量
|
||
$count: "count"
|
||
}
|
||
]).toArray();
|
||
|
||
const unpay = result.length > 0 && result[0].count ? result[0].count : 0;
|
||
const unpayDeliveryFee = await db.collection('medicine-purchase-order').countDocuments({ accountId, shippingFee: { $gt: 0 }, status: 'ybPaid' });
|
||
return { success: true, message: "查询成功", data: { unpay, unpayDeliveryFee } };
|
||
} catch (e) {
|
||
return { success: false, message: `获取患者首页处方数量失败: ${e.message}` }
|
||
}
|
||
}
|
||
|
||
async function getReUploadRxList(ctx) {
|
||
try {
|
||
const pharmacistNo = typeof ctx.pharmacistNo === 'string' && ctx.pharmacistNo.trim() !== '' ? ctx.pharmacistNo.trim() : '';
|
||
if (!pharmacistNo) {
|
||
return { success: false, message: "药师编号不能为空" };
|
||
}
|
||
const query = {
|
||
prescriptionType: 'storeMedicinePurchase',
|
||
pharmacistNo,
|
||
needReUpload: true,
|
||
// createTime: {
|
||
// $gt: dayjs().startOf('day').valueOf()
|
||
// }
|
||
}
|
||
const list = await db.collection('diagnostic-record').find(query, { projection: { _id: 1, auditTime: 1, name: 1 } }).toArray();
|
||
const total = await db.collection('diagnostic-record').countDocuments(query);
|
||
return { success: true, message: "查询成功", data: list.map(i => decryptDiagnosticRecordFields(i)), total };
|
||
} catch (e) {
|
||
return { success: false, message: `获取需要重新上传的处方列表失败: ${e.message}` }
|
||
}
|
||
}
|
||
|
||
async function handleReUploadRx(ctx) {
|
||
try {
|
||
const pharmacistNo = typeof ctx.pharmacistNo === 'string' && ctx.pharmacistNo.trim() !== '' ? ctx.pharmacistNo.trim() : '';
|
||
const ids = Array.isArray(ctx.ids) ? ctx.ids.filter(i => typeof i == 'string' && ObjectId.isValid(i)) : [];
|
||
if (!pharmacistNo || ids.length === 0) {
|
||
return { success: false, message: "药师编号或处方id不能为空" };
|
||
}
|
||
const { matchedCount } = await db.collection('diagnostic-record').updateMany({ _id: { $in: ids.map(i => new ObjectId(i)) }, prescriptionType: 'storeMedicinePurchase', pharmacistNo, needReUpload: true }, { $set: { needReUpload: false } });
|
||
if (matchedCount) {
|
||
return { success: true, message: "操作成功" };
|
||
}
|
||
return { success: false, message: "操作失败" };
|
||
} catch (e) {
|
||
return { success: true, message: e.message }
|
||
}
|
||
}
|
||
|
||
async function getPatientWineBuyCount(ctx, db) {
|
||
try {
|
||
const wineId = typeof ctx.wineId === 'string' && ctx.wineId.trim() !== '' ? ctx.wineId.trim() : '';
|
||
const patientId = typeof ctx.patientId === 'string' && ctx.patientId.trim() !== '' ? ctx.patientId.trim() : '';
|
||
if (!wineId || !patientId) {
|
||
return { success: false, message: "酒类id或患者id号不能为空" };
|
||
}
|
||
const query = {
|
||
prescriptionType: 'medicalWinePurchase',
|
||
"wines._id": wineId,
|
||
patientId,
|
||
}
|
||
const count = await db.collection('diagnostic-record').countDocuments(query);
|
||
return { success: true, message: "查询成功", data: count };
|
||
} catch (e) {
|
||
return { success: false, message: `查询患者购买酒类次数失败: ${e.message}` }
|
||
}
|
||
} |