534 lines
18 KiB
JavaScript
534 lines
18 KiB
JavaScript
/**
|
||
* 线上购药药品库 online-drug-info
|
||
* 与互联网医院药品库(drug-info)字段、功能保持一致,但使用独立的集合
|
||
*/
|
||
const dayjs = require("dayjs");
|
||
const { ObjectId } = require("mongodb");
|
||
const { getDrugList } = require("../drug-info/verify");
|
||
const validator = require("../../utils/validator");
|
||
|
||
exports.main = async (item, db) => {
|
||
switch (item.type) {
|
||
// 供患者/医生侧检索使用的简单查询(已在线使用,保留)
|
||
case "getOnlineDrugList":
|
||
return await getOnlineDrugList(item, db);
|
||
case "getOnlineSimilarDrugInfo":
|
||
return await getOnlineSimilarDrugInfo(item, db);
|
||
// 供后台管理使用的完整管理接口
|
||
case "getOnlineDrugInfo":
|
||
return await getOnlineDrugInfo(item, db);
|
||
case "addOnlineDrugInfo":
|
||
return await addOnlineDrugInfo(item, db);
|
||
case "editOnlineDrugInfo":
|
||
return await editOnlineDrugInfo(item, db);
|
||
case "setOnlineDrugOnSale":
|
||
return await setOnlineDrugOnSale(item, db);
|
||
case "importOnlineDrugInfo":
|
||
return await importOnlineDrugInfo(item, db);
|
||
case "checkOnlineDrugInfoStock":
|
||
return await checkOnlineDrugInfoStock(item, db);
|
||
case "getOnlineDrugRepeatTags":
|
||
return await getOnlineDrugRepeatTags(item, db);
|
||
default:
|
||
return { success: false, message: "未找到接口" };
|
||
}
|
||
};
|
||
|
||
// 患者/医生端使用:关键字+在售过滤
|
||
async function getOnlineDrugList(ctx, db) {
|
||
try {
|
||
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 keyword =
|
||
typeof ctx.keyword === "string" ? ctx.keyword.trim() : "";
|
||
const query = {};
|
||
const onSale = typeof ctx.onSale === "boolean" ? ctx.onSale : true;
|
||
query.onSale = onSale;
|
||
let fuzzyQuery = null;
|
||
if (keyword) {
|
||
fuzzyQuery = {
|
||
$or: [
|
||
{ name: new RegExp(keyword.trim(), "i") },
|
||
{ generic_name: new RegExp(keyword.trim(), "i") },
|
||
{ barcode: keyword.trim() },
|
||
{ pinyin_code: new RegExp(keyword.trim(), "i") },
|
||
{ product_id_str: keyword.trim() },
|
||
],
|
||
};
|
||
}
|
||
|
||
if (Array.isArray(ctx.ids)) {
|
||
const ids = ctx.ids
|
||
.map((i) =>
|
||
typeof i === "string" && i.trim()
|
||
? new ObjectId(i.trim())
|
||
: null
|
||
)
|
||
.filter((i) => !!i);
|
||
if (ids.length) {
|
||
query._id = { $in: ids };
|
||
}
|
||
}
|
||
const applyQuery = fuzzyQuery ? { $and: [query, fuzzyQuery] } : query;
|
||
const [total, list] = await Promise.all([
|
||
db.collection("online-drug-info").countDocuments(applyQuery),
|
||
db
|
||
.collection("online-drug-info")
|
||
.find(applyQuery)
|
||
.skip((page - 1) * pageSize)
|
||
.limit(pageSize)
|
||
.toArray(),
|
||
]);
|
||
return {
|
||
success: true,
|
||
message: "查询成功",
|
||
data: list,
|
||
total,
|
||
pages: Math.ceil(total / pageSize),
|
||
};
|
||
} catch (e) {
|
||
return { success: false, message: e.message };
|
||
}
|
||
}
|
||
|
||
// 后台管理:分页查询(字段与 getHlwDrugInfo 一致)
|
||
async function getOnlineDrugInfo(ctx, db) {
|
||
try {
|
||
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 = {};
|
||
|
||
if (typeof ctx.name === "string" && ctx.name.trim()) {
|
||
query.name = new RegExp(ctx.name.trim(), "i");
|
||
}
|
||
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.barcode === "string" && ctx.barcode.trim()) {
|
||
query.barcode = ctx.barcode.trim();
|
||
}
|
||
if (
|
||
typeof ctx.insuranceCode === "string" &&
|
||
ctx.insuranceCode.trim()
|
||
) {
|
||
query.insurance_code = ctx.insuranceCode.trim();
|
||
}
|
||
|
||
if (typeof ctx.hisDrugCode === "string" && ctx.hisDrugCode.trim()) {
|
||
query.his_drug_code = ctx.hisDrugCode.trim();
|
||
}
|
||
|
||
if (typeof ctx.applyPeople === "string" && ctx.applyPeople.trim()) {
|
||
query.apply_people = ctx.applyPeople.trim();
|
||
}
|
||
|
||
const res = await db
|
||
.collection("online-drug-info")
|
||
.find(query)
|
||
.sort({ onSale: -1, createTime: -1 })
|
||
.skip((page - 1) * pageSize)
|
||
.limit(pageSize)
|
||
.toArray();
|
||
const total = await db
|
||
.collection("online-drug-info")
|
||
.countDocuments(query);
|
||
|
||
return {
|
||
success: true,
|
||
message: "查询成功",
|
||
data: res,
|
||
total,
|
||
pages: Math.ceil(total / pageSize),
|
||
};
|
||
} catch (e) {
|
||
return { success: false, message: e.message };
|
||
}
|
||
}
|
||
|
||
// 新增线上药品
|
||
async function addOnlineDrugInfo(ctx, db) {
|
||
const { params = {} } = ctx;
|
||
if (Object.prototype.toString.call(params) !== "[object Object]") {
|
||
return { success: false, message: "参数错误" };
|
||
}
|
||
try {
|
||
const peopleInfo = {
|
||
creator: typeof ctx.operator === "string" ? ctx.operator : "",
|
||
creatorId:
|
||
typeof ctx.operatorId === "string" ? ctx.operatorId : "",
|
||
createTime: Date.now(),
|
||
};
|
||
const { onSale, ...data } = params;
|
||
let newDrug = {};
|
||
if (Object.keys(data).length) {
|
||
if (!data.his_drug_code || !String(data.his_drug_code).trim()) {
|
||
return { success: false, message: "ERP编码不能为空" };
|
||
}
|
||
if (data.his_drug_code) {
|
||
const count = await db.collection("online-drug-info").countDocuments({ his_drug_code: data.his_drug_code });
|
||
if (count > 0) {
|
||
return { success: false, message: "ERP编码已存在" };
|
||
}
|
||
}
|
||
const [item] = getDrugList([data], true, peopleInfo);
|
||
const { msgs, ...drug } = item;
|
||
if (msgs.length) {
|
||
return { success: false, message: msgs.join(",") };
|
||
}
|
||
newDrug = { ...drug };
|
||
}
|
||
if (typeof onSale === "boolean") {
|
||
newDrug.onSale = onSale;
|
||
}
|
||
newDrug.createTime = Date.now();
|
||
const res = await db
|
||
.collection("online-drug-info")
|
||
.insertOne(newDrug);
|
||
return {
|
||
success: true,
|
||
message: "新增药品成功",
|
||
id: res.insertedId,
|
||
};
|
||
} catch (e) {
|
||
return {
|
||
success: false,
|
||
message: e.message || "新增药品失败",
|
||
};
|
||
}
|
||
}
|
||
|
||
// 编辑线上药品
|
||
async function editOnlineDrugInfo(ctx, db) {
|
||
const { id, params = {} } = ctx;
|
||
if (
|
||
Object.prototype.toString.call(params) !== "[object Object]" ||
|
||
typeof id !== "string"
|
||
) {
|
||
return { success: false, message: "参数错误" };
|
||
}
|
||
try {
|
||
const { onSale, ...data } = params;
|
||
let updateData = {};
|
||
if (Object.keys(data).length) {
|
||
if ("his_drug_code" in data && !String(data.his_drug_code).trim()) {
|
||
return { success: false, message: "ERP编码不能为空" };
|
||
}
|
||
if (data.his_drug_code) {
|
||
const count = await db.collection("online-drug-info").countDocuments({ his_drug_code: data.his_drug_code, _id: { $ne: new ObjectId(id) } });
|
||
if (count > 0) {
|
||
return { success: false, message: "ERP编码已存在" };
|
||
}
|
||
}
|
||
const peopleInfo = {
|
||
updater: typeof ctx.operator === "string" ? ctx.operator : "",
|
||
updaterId:
|
||
typeof ctx.operatorId === "string" ? ctx.operatorId : "",
|
||
updateTime: Date.now(),
|
||
};
|
||
const [item] = getDrugList([data], false, peopleInfo);
|
||
const { msgs, ...drug } = item;
|
||
if (msgs.length) {
|
||
return { success: false, message: msgs.join(",") };
|
||
}
|
||
updateData = { ...drug };
|
||
}
|
||
if (typeof onSale === "boolean") {
|
||
updateData.onSale = onSale;
|
||
}
|
||
if (Object.keys(updateData).length) {
|
||
updateData.updateTime = Date.now();
|
||
const res = await db
|
||
.collection("online-drug-info")
|
||
.updateOne(
|
||
{ _id: new ObjectId(id) },
|
||
{ $set: updateData }
|
||
);
|
||
if (res.matchedCount === 0) {
|
||
return { success: false, message: "未找到对应药品" };
|
||
}
|
||
}
|
||
return { success: true, message: "更新成功" };
|
||
} catch (e) {
|
||
console.log("editOnlineDrugInfo error", e);
|
||
return { success: false, message: e.message };
|
||
}
|
||
}
|
||
|
||
// 批量上/下架
|
||
async function setOnlineDrugOnSale(ctx, db) {
|
||
const { ids, onSale, updaterId, updater } = ctx;
|
||
const _ids = Array.isArray(ids)
|
||
? ids
|
||
.filter((i) => typeof i === "string" && i.trim())
|
||
.map((i) => new ObjectId(i.trim()))
|
||
: [ids];
|
||
if (
|
||
_ids.length === 0 ||
|
||
typeof onSale !== "boolean" ||
|
||
typeof updaterId !== "string" ||
|
||
typeof updater !== "string" ||
|
||
!updaterId.trim() ||
|
||
!updater.trim()
|
||
) {
|
||
return { success: false, message: "参数错误" };
|
||
}
|
||
try {
|
||
const res = await db
|
||
.collection("online-drug-info")
|
||
.updateMany(
|
||
{ _id: { $in: _ids } },
|
||
{
|
||
$set: {
|
||
onSale,
|
||
updateTime: Date.now(),
|
||
updaterId,
|
||
updater,
|
||
},
|
||
}
|
||
);
|
||
return { success: true, message: "更新成功", res };
|
||
} catch (e) {
|
||
console.log("setOnlineDrugOnSale error", e);
|
||
return { success: false, message: e.message };
|
||
}
|
||
}
|
||
|
||
// 批量导入(用于程序接口调用,CSV 上传走单独 upload-online-drug-csv)
|
||
async function importOnlineDrugInfo(ctx, db) {
|
||
const drugs = Array.isArray(ctx.drugs)
|
||
? ctx.drugs.filter((i) => typeof i === "object" && i !== null)
|
||
: [];
|
||
const onSale =
|
||
typeof ctx.onSale === "boolean" ? ctx.onSale : true;
|
||
if (drugs.length === 0) {
|
||
return { success: false, message: "参数错误" };
|
||
}
|
||
try {
|
||
const peopleInfo = {
|
||
creator: typeof ctx.operator === "string" ? ctx.operator : "",
|
||
creatorId:
|
||
typeof ctx.operatorId === "string" ? ctx.operatorId : "",
|
||
createTime: Date.now(),
|
||
};
|
||
const datalist = getDrugList(drugs, true, peopleInfo);
|
||
datalist.forEach((i) => {
|
||
if (!i.his_drug_code || !String(i.his_drug_code).trim()) {
|
||
i.msgs.push("ERP编码不能为空");
|
||
}
|
||
});
|
||
const createTime = dayjs().valueOf();
|
||
const list = datalist
|
||
.filter((i) => i.msgs.length === 0)
|
||
.map((i) => {
|
||
const { msgs, ...drug } = i;
|
||
drug.createTime = createTime;
|
||
drug.onSale = onSale;
|
||
return drug;
|
||
});
|
||
if (!list.length) {
|
||
return { success: false, message: "导入失败" };
|
||
}
|
||
const res = await db
|
||
.collection("online-drug-info")
|
||
.insertMany(list);
|
||
if (res.insertedCount > 0) {
|
||
return {
|
||
success: true,
|
||
message: `成功导入${res.insertedCount}条数据`,
|
||
};
|
||
}
|
||
return { success: false, message: "导入失败" };
|
||
} catch (e) {
|
||
console.log("importOnlineDrugInfo error", e);
|
||
return { success: false, message: e.message };
|
||
}
|
||
}
|
||
async function getOnlineSimilarDrugInfo(ctx, db) {
|
||
try {
|
||
const accountId = typeof ctx.accountId === "string" && ctx.accountId.trim() !== "" ? ctx.accountId.trim() : "";
|
||
const patientId = typeof ctx.patientId === "string" && ctx.patientId.trim() !== "" ? ctx.patientId.trim() : "";
|
||
const patientName = typeof ctx.patientName === "string" && ctx.patientName.trim() !== "" ? ctx.patientName.trim() : "";
|
||
const phone = typeof ctx.phone === "string" && ctx.phone.trim() !== "" ? ctx.phone.trim() : "";
|
||
const drugs = Array.isArray(ctx.drugs) ? ctx.drugs.filter(i => i && typeof i.insurance_code == 'string' && i.insurance_code.trim() !== "") : [];
|
||
const insurance_codes = drugs.map(i => i.insurance_code);
|
||
const query = { insurance_code: { $in: insurance_codes }, onSale: true };
|
||
const medicines = await db.collection("online-drug-info").find(query).toArray();
|
||
const lackMedicines = drugs.filter(i => !medicines.some(med => med.insurance_code === i.insurance_code));
|
||
let similarDrugs = [];
|
||
if (lackMedicines.length > 0) {
|
||
const lackMedicineRegApi = require("../lack-medicine-reg");
|
||
// 补全厂商数据:从药品库(包括已下架)中查询完整信息
|
||
const lackInsuranceCodes = lackMedicines.map(i => i.insurance_code);
|
||
const allDrugsInDb = await db.collection("online-drug-info").find({
|
||
insurance_code: { $in: lackInsuranceCodes }
|
||
}).toArray();
|
||
const lackDrugsWithManufacturer = lackMedicines.map(drug => {
|
||
// 尝试从数据库中找到匹配的药品信息
|
||
const dbDrug = allDrugsInDb.find(d => d.insurance_code === drug.insurance_code);
|
||
return {
|
||
...drug,
|
||
drugName: drug.drugName || (dbDrug ? dbDrug.name : ''),
|
||
spec: drug.spec || (dbDrug ? dbDrug.specification : ''),
|
||
manufacturer: drug.manufacturer || (dbDrug ? dbDrug.manufacturer : ''),
|
||
insurance_code: drug.insurance_code
|
||
};
|
||
});
|
||
const res = await lackMedicineRegApi({
|
||
type: "batchAddLackMedicineReg",
|
||
drugs: lackDrugsWithManufacturer,
|
||
accountId,
|
||
patientId,
|
||
patientName,
|
||
phone,
|
||
inventory: 'online',
|
||
regType: 'outOfStock',
|
||
corpId: ctx.corpId,
|
||
}, db)
|
||
const similarQuery = lackMedicines.map(i => ({
|
||
$and: [
|
||
{ onSale: true, insurance_code: new RegExp(i.insurance_code.slice(0, -5), "i") },
|
||
{ insurance_code: { $ne: i.insuranceCode } }
|
||
]
|
||
}))
|
||
similarDrugs = await db.collection("online-drug-info").find({ $or: similarQuery }).toArray();
|
||
}
|
||
return { success: true, message: "查询成功", drugs: medicines, similarDrugs }
|
||
} catch (e) {
|
||
return { success: false, message: e.message }
|
||
}
|
||
}
|
||
|
||
async function checkOnlineDrugInfoStock(ctx, db) {
|
||
try {
|
||
const accountId = typeof ctx.accountId === 'string' ? ctx.accountId.trim() : '';
|
||
const patientId = typeof ctx.patientId === 'string' ? ctx.patientId.trim() : '';
|
||
const patientName = typeof ctx.patientName === 'string' ? ctx.patientName.trim() : '';
|
||
const phone = typeof ctx.phone === 'string' ? ctx.phone.trim() : '';
|
||
const drugs = Array.isArray(ctx.drugs) ? ctx.drugs.map(i => ({
|
||
insurance_code: i && typeof i.insurance_code === 'string' ? i.insurance_code.trim() : '',
|
||
num: i && Number(i.num) > 0 && Number.isInteger(Number(i.num)) ? Number(i.num) : 0,
|
||
})) : [];
|
||
const areaCode = typeof ctx.areaCode === 'string' ? ctx.areaCode.trim() : '';
|
||
|
||
if (accountId.length === 0) {
|
||
return { success: false, message: "账号id不能为空" }
|
||
}
|
||
if (accountId.length > 50) {
|
||
return { success: false, message: "账号id不能超过50个字" }
|
||
}
|
||
if (areaCode.length === 0) {
|
||
return { success: false, message: "地区编码不能为空" }
|
||
}
|
||
if (patientId.length > 50) {
|
||
return { success: false, message: "患者id不能超过50个字" }
|
||
}
|
||
if (patientName.length === 0) {
|
||
return { success: false, message: "患者姓名不能为空" }
|
||
}
|
||
if (patientName.length > 30) {
|
||
return { success: false, message: "患者姓名不能超过30个字" }
|
||
}
|
||
// 手机号验证:如果提供了手机号则必须格式正确
|
||
if (phone.length > 0 && !validator.isMobile(phone)) {
|
||
return { success: false, message: "患者手机号格式不正确" }
|
||
}
|
||
if (drugs.length === 0 || drugs.length > 5) {
|
||
return { success: false, message: "药品数量不能为空且不能超过5个" }
|
||
}
|
||
if (drugs.some(i => !(i.insurance_code && i.num))) {
|
||
return { success: false, message: "药品信息不完整" }
|
||
}
|
||
const list = await db.collection("online-drug-info").find({ insurance_code: { $in: drugs.map(i => i.insurance_code) } }, { projection: { name: 1, specification: 1, manufacturer: 1, insurance_code: 1 } }).toArray();
|
||
const arr = drugs.map(i => {
|
||
const drug = list.find(m => m.insurance_code === i.insurance_code);
|
||
return { drug, num: i.num, insurance_code: i.insurance_code }
|
||
})
|
||
const unvalid = arr.find(i => !i.drug);
|
||
if (unvalid) {
|
||
return { success: false, message: `药品${unvalid.insurance_code}不存在` }
|
||
}
|
||
const zytHis = require("../zyt-his")
|
||
const res = await zytHis({ type: 'getHisDrugInfo', areaCode, insuranceCodes: drugs.map(i => i.insurance_code) });
|
||
if (!res || !res.success) {
|
||
return { success: false, message: res && res.message ? res.message : '查询his库存信息失败' }
|
||
}
|
||
const hisDrugs = Array.isArray(res.data) ? res.data : [];
|
||
const lackMedicines = [];
|
||
const result = [];
|
||
arr.forEach(i => {
|
||
const hisDrug = hisDrugs.find(m => m.gjbm === i.insurance_code);
|
||
const stock = hisDrug && Number(hisDrug.stock_amount) > 0 ? Number(hisDrug.stock_amount) : 0;
|
||
const price = hisDrug && Number(hisDrug.pack_retprice) > 0 ? Number(hisDrug.pack_retprice) : undefined;
|
||
if (stock < i.num) {
|
||
lackMedicines.push({
|
||
drugName: i.drug.name,
|
||
spec: i.drug.specification,
|
||
manufacturer: i.drug.manufacturer,
|
||
insuranceCode: i.drug.insurance_code
|
||
})
|
||
}
|
||
result.push({ insuranceCode: i.insurance_code, stock, price })
|
||
})
|
||
if (lackMedicines.length > 0) {
|
||
const lackMedicineRegApi = require("../lack-medicine-reg");
|
||
lackMedicineRegApi({
|
||
type: "batchAddLackMedicineReg",
|
||
drugs: lackMedicines,
|
||
accountId,
|
||
patientId,
|
||
patientName,
|
||
phone,
|
||
inventory: 'online',
|
||
regType: 'lowStock',
|
||
corpId: ctx.corpId,
|
||
}, db)
|
||
}
|
||
return { success: true, message: '查询成功', data: result }
|
||
} catch (e) {
|
||
return { success: false, message: e.message }
|
||
}
|
||
}
|
||
|
||
async function getOnlineDrugRepeatTags(ctx, db) {
|
||
try {
|
||
const pipeline = [
|
||
{
|
||
$match: {
|
||
repeatTag: {
|
||
$type: "array",
|
||
$ne: []
|
||
}
|
||
}
|
||
},
|
||
{ $unwind: "$repeatTag" },
|
||
{ $match: { repeatTag: { $type: "number", $gt: 0 } } },
|
||
{ $group: { _id: "$repeatTag" } },
|
||
{ $sort: { _id: 1 } },
|
||
{
|
||
$group: {
|
||
_id: null,
|
||
uniqueTags: { $push: "$_id" }
|
||
}
|
||
},
|
||
{ $project: { _id: 0, uniqueTags: 1 } }
|
||
];
|
||
const result = await db.collection("online-drug-info").aggregate(pipeline).toArray();
|
||
const uniqueTags = result.length > 0 ? result[0].uniqueTags : [];
|
||
return { success: true, message: '查询成功', data: uniqueTags }
|
||
} catch (e) {
|
||
return { success: false, message: e.message }
|
||
}
|
||
|
||
}
|