const dayjs = require("dayjs"); const setCountText = require("./order-status"); const { ObjectId } = require("mongodb"); const unionPay = require("../union-pay"); module.exports = async (item, db) => { const builded = process.env.IS_BUILDED === 'YES'; switch (item.statsType) { case "order": return await getOrderCount(item, db); case "doctorOrder": return await getDoctorOrderCount(item, db); case "getStoreMatePadRxStats": return await getStoreMatePadRxStats(item, db); case "zyStoreRx": return await getZyStoreRxCount(item, db); // case "oldRx": // return await getZyStoreAgeRxCount(item, db); case "getPayStatus": return await getHisPayStatus(item, db); case "rxDurationStats": return await getRxDurationStats(item, db); case "drugStats": return await getDrugStats(item, db); case "createAlipayOrder": return builded ? null : await createAlipayOrder(item, db); case "queryAlipayOrder": return await queryAlipayOrder(item, db); case "refundAlipayOrder": return builded ? null : await refundAlipayOrder(item, db); case "fengniaoTest": return await fengniaoTest(item, db); case "emsTest": return await emsTest(item, db); case "hisTest": return await hisTest(item, db); case "lackTest": return await lackTest(item, db); case "purchaseTest": return await purchaseTest(item, db); case "testMedicineNotify": return await testMedicineNotify(item, db); case "tradeTest": return await tradeTest(item, db); case "unionTest": // return await unionPay.main({ type: "refundUnionPayBill", billNo: '3JHR202604091344UN' , billDate: Date.now(), refundAmount: 100 }); // return await unionPay.main({ type: "getUnionPayQrcode", billNo: '3JHR202604091344UN' , billDate: Date.now(), totalAmount: 100 }); case "wineTest": return await wineTest(item, db); default: return { success: false, message: "未知统计类型!!" }; } }; function getDateRange({ dates }) { console.log("dates", dates); const dateArr = typeof dates === "string" ? dates .split(",") .filter((i) => i && dayjs(i).isValid()) .map((i) => dayjs(i)) : []; console.log("dateArr", dateArr); const [dateStart = dayjs(), dateEnd = dayjs(dateStart)] = dateArr; const minDate = dateStart.isBefore(dateEnd) ? dateStart : dateEnd; const maxDate = dateStart.isBefore(dateEnd) ? dateEnd : dateStart; if (Math.abs(minDate.diff(maxDate, "day")) > 30) { return [ minDate.startOf("day").valueOf(), minDate.add(31, "day").endOf("day").valueOf(), ]; } console.log("minDate", minDate); console.log("maxDate", maxDate); return [minDate.startOf("day").valueOf(), maxDate.endOf("day").valueOf()]; } async function getOrderCount(params, db) { try { const [startTime, endTime] = params; const startTimeTemp = dayjs(startTime).startOf("day").valueOf(); const endTimeTemp = dayjs(endTime).endOf("day").valueOf(); const data = await db .collection("consult-order") .aggregate([ { $match: { createTime: { $gte: startTimeTemp, $lte: endTimeTemp } } }, { $group: { _id: { orderStatus: "$orderStatus", payStatus: "$payStatus", }, count: { $sum: 1 }, // 统计每个组合的数量 }, }, { $project: { _id: 0, // 如果不需要显示 _id orderStatus: "$_id.orderStatus", payStatus: "$_id.payStatus", count: 1, }, }, ]) .toArray(); const res = setCountText(data); const allCount = res.reduce((acc, cur) => acc + cur.count, 0); return { success: true, message: "查询成功", startDate: dayjs(startTime).format("YYYY-MM-DD HH:mm:ss"), endDate: dayjs(endTime).format("YYYY-MM-DD HH:mm:ss"), allCount: allCount, data: res, }; } catch (err) { return { success: false, message: err.message }; } } async function getDoctorOrderCount(params, db) { try { const [startTime, endTime] = getDateRange(params); const doctorCount = await db .collection("hlw-doctor") .countDocuments({ job: "doctor" }); const doctorList = await db .collection("hlw-doctor") .find( { job: "doctor" }, { projection: { doctorName: 1, doctorNo: 1, _id: 0 } } ) .toArray(); const data = await db .collection("consult-order") .aggregate([ { $match: { createTime: { $gte: startTime, $lte: endTime } } }, { $group: { _id: { orderStatus: "$orderStatus", payStatus: "$payStatus", doctorCode: "$doctorCode", }, count: { $sum: 1 }, // 统计每个组合的数量 }, }, { $project: { _id: 0, // 如果不需要显示 _id doctorCode: "$_id.doctorCode", orderStatus: "$_id.orderStatus", payStatus: "$_id.payStatus", count: 1, }, }, ]) .toArray(); const res = setCountText(data, doctorList); const allCount = res.reduce((acc, cur) => acc + (cur.allCount || 0), 0); return { success: true, message: "查询成功", startDate: dayjs(startTime).format("YYYY-MM-DD HH:mm:ss"), endDate: dayjs(endTime).format("YYYY-MM-DD HH:mm:ss"), allCount, doctorCount, statsCount: res.length, data: res, }; } catch (err) { return { success: false, message: err.message }; } } async function getStoreCount(params, db) { try { let storeIds = []; const [startTime, endTime] = getDateRange(params); if (params.dataType === "storeOrder" || params.dataType === "storeRx") { if (typeof params.area !== "string" || params.area.trim() === "") { return { success: false, message: "区域不能为空" }; } const stores = await db .collection("store-list") .find( { "region.2": params.area }, { projection: { store_id: 1, _id: 0 } } ) .toArray(); storeIds = stores.map((i) => i.store_id); } // 统计越城区药店的今日订单数量 start if (params.dataType === "storeOrder") { const storeOrderStats = await db .collection("consult-order") .aggregate([ { $match: { drugStoreNo: { $in: storeIds }, createTime: { $gte: startTime, $lte: endTime }, }, }, { $group: { _id: "$drugStoreNo", drugStoreName: { $first: "$drugStoreName" }, count: { $sum: 1 }, // 统计每个组合的数量 }, }, ]) .toArray(); const storeOrderRes = Array.from(storeOrderStats.values()); return storeOrderRes; } if (params.dataType === "storeRx") { // 统计药店的处方数量 start const orders = await db .collection("consult-order") .find( { drugStoreNo: { $in: storeIds }, createTime: { $gte: startTime, $lte: endTime }, }, { projection: { orderId: 1, _id: 0, drugStoreName: 1, drugStoreNo: 1, }, } ) .toArray(); const orderIds = orders.map((i) => i.orderId); const record = await db .collection("diagnostic-record") .aggregate([ { $match: { orderId: { $in: orderIds }, status: "PASS" } }, { $group: { _id: "$orderId", count: { $sum: 1 }, // 统计每个组合的数量 }, }, ]) .toArray(); const om = record.reduce((acc, cur) => { acc[cur._id] = cur.count; return acc; }, {}); const rxOrders = orders.filter((i) => om[i.orderId] > 0); const storeRx = rxOrders.reduce((acc, cur) => { const data = acc.get(cur.drugStoreNo) || { drugStoreNo: cur.drugStoreNo, drugStoreName: cur.drugStoreName, count: 0, }; data.count += 1; acc.set(cur.drugStoreNo, data); return acc; }, new Map()); const storeRxRes = Array.from(storeRx.values()); // 统计不同类型药店的数据 const typeStore = storeRxRes.reduce( (acc, cur) => { if (cur.drugStoreName.startsWith("浙江震元")) { acc.zy += cur.count; } else { acc.nzy += cur.count; } return acc; }, { zy: 0, nzy: 0 } ); return { ...typeStore, storeRxRes }; } if (params.dataType === "doctorRx") { // 统计医生的处方数量 start const doctorRx = await db .collection("diagnostic-record") .aggregate([ { $match: { createTime: { $gte: startTime, $lte: endTime }, status: "PASS", _id: { $nin: [ new ObjectId("67eca29fecce43c72c51d1e7"), new ObjectId("67ecfe26ecce43c72c520ec7"), ], }, }, }, { $group: { _id: "$doctorCode", doctorName: { $first: "$doctorName" }, count: { $sum: 1 }, // 统计每个组合的数量 }, }, ]) .toArray(); return doctorRx; } } catch (e) { return { success: false, message: e.message }; } } async function getPeriodRxCount(params, db) { const data = params.date && dayjs(params.period).isValid() ? dayjs(params.date).format("YYYY-MM-DD") : dayjs().format("YYYY-MM-DD"); try { let time = dayjs().startOf("day").format("YYYY-MM-DD HH:mm:ss"); const group = []; while (dayjs(time).isBefore(dayjs().endOf("day"))) { group.push([ dayjs(time).valueOf(), dayjs(time).add(30, "minutes").valueOf(), ]); // group.push({ $match: { createTime: { $gte: dayjs(time).format('YYYY-MM-DD HH:mm:ss'), $lt: dayjs(time).add(30, 'minutes').format('YYYY-MM-DD HH:mm:ss') }, status: 'PASS' } }) time = dayjs(time).add(30, "minutes").format("YYYY-MM-DD HH:mm:ss"); } const result = []; for (let [start, end] of group) { const total = await db.collection("diagnostic-record").countDocuments({ createTime: { $gte: start, $lt: end }, status: "PASS", }); result.push({ start: dayjs(start).format("YYYY-MM-DD HH:mm:ss"), end: dayjs(end).format("YYYY-MM-DD HH:mm:ss"), total, }); } return result; } catch (e) { return { success: false, message: e.message }; } } async function getStoreMatePadRxStats(params, db) { const [startTime, endTime] = getDateRange(params); try { const res = await db .collection("diagnostic-record") .aggregate([ { $match: { orderSource: "MATEPAD", createTime: { $gte: startTime, $lte: endTime }, status: "PASS", }, }, { $group: { _id: "$drugStoreNo", count: { $sum: 1 }, }, }, ]) .toArray(); const stores = await db .collection("store-list") .find( { store_id: { $in: res.map((i) => i._id) } }, { projection: { store_id: 1, name: 1, _id: 0 } } ) .toArray(); return { success: true, data: res, stores }; } catch (e) { return { success: false, message: e.message }; } } async function getZyStoreRxCount(ctx, db) { const [startTime, endTime, storeType, source] = getDateRange(ctx); const storeTypes = storeType === "other" ? ["其他"] : ["加盟", "直营"]; try { const stores = await db .collection("store-list") .find( { type: { $in: storeTypes } }, { projection: { store_id: 1, name: 1, _id: 0 } } ) .toArray(); const storeIds = stores.map((i) => i.store_id); const m = stores.reduce((acc, cur) => { // console.log(cur.store_id, cur.name) acc.set(cur.store_id, cur.name); return acc; }, new Map()); const matches = { drugStoreNo: { $in: storeIds }, createTime: { $gte: startTime, $lte: endTime }, status: "PASS", }; if (source) { matches.orderSource = source; } const res = await db .collection("diagnostic-record") .aggregate([ { $match: matches }, { $addFields: { date: { $dateToString: { format: "%Y-%m-%d", date: { $toDate: "$createTime" }, timezone: "Asia/Shanghai", // 设置为中国标准时间 }, }, }, }, { $group: { _id: { date: "$date", id: "$drugStoreNo" }, date: { $first: "$date" }, drugStoreNo: { $first: "$drugStoreNo" }, count: { $sum: 1 }, }, }, { $project: { _id: 0, date: 1, drugStoreNo: 1, count: 1, }, }, ]) .toArray(); res.forEach((i) => { i.name = m.get(i.drugStoreNo) || ""; }); return res; } catch (e) { return { success: false, message: e.message }; } } async function getZyStoreAgeRxCount(ctx, db) { const [startTime, endTime] = getDateRange(ctx); try { const query = { createTime: { $gte: startTime, $lte: endTime } }; if (ctx.ageType === "old") { query.age = { $gte: 70 }; } else { query.age = { $lt: 70 }; } const orders = await db .collection("consult-order") .find(query, { projection: { orderId: 1, _id: 0 } }) .toArray(); const orderIds = orders.map((i) => i.orderId); const rxCountGroup = await db .collection("diagnostic-record") .aggregate([ { $match: { createTime: { $gte: startTime, $lte: endTime }, status: "PASS", orderId: { $in: orderIds }, }, }, { $group: { _id: "$drugStoreNo", drugStoreNo: { $first: "$drugStoreNo" }, count: { $sum: 1 }, }, }, ]) .toArray(); const stores = await db .collection("store-list") .find( { store_id: { $in: rxCountGroup.map((i) => i.drugStoreNo) } }, { projection: { store_id: 1, name: 1, _id: 0, type: 1, region: 1 } } ) .toArray(); const m = stores.reduce((acc, cur) => { acc.set(cur.store_id, cur); return acc; }, new Map()); rxCountGroup.forEach((i) => { const store = m.get(i.drugStoreNo) || null; if (store) { i.name = store.name || ""; i.type = store.type || ""; i.area = Array.isArray(store.region) ? store.region[2] : ""; } else { i.name = ""; i.type = ""; i.area = ""; } }); return rxCountGroup; } catch (e) { return { success: false, message: e.message }; } } async function getHisPayStatus(ctx) { // patientId, registerId, medorgOrderNo const { patientId, registerId } = ctx; const hisApi = require("../zyt-his"); const res = await hisApi({ type: "getPayStatus", patientId, registerId, medorgOrderNo: registerId, }); if (res.success) { return { success: true, data: res }; } else { return { success: false, message: res.message || "查询失败" }; } } async function refundFee(ctx) { // patientId, registerId, medorgOrderNo const { patientId, registerId } = ctx; const hisApi = require("../zyt-his"); const res = await hisApi({ type: "hlwRefund", patientId, registerId, medorgOrderNo: registerId, }); if (res.success) { return { success: true, data: res }; } else { return { success: false, message: res.message || "查询失败" }; } } async function getRxDurationStats(params, db) { const { startTime, endTime } = params; const startTimeTemp = dayjs(startTime).startOf("day").valueOf(); const endTimeTemp = dayjs(endTime).endOf("day").valueOf(); const pipeline = [ { $match: { // age: { $gte: 60 }, createTime: { $gte: startTimeTemp, $lte: endTimeTemp }, }, }, { $project: { orderId: 1, _id: 0, }, }, { $lookup: { from: "hlw-doctor-times", localField: "orderId", foreignField: "orderId", as: "doctorTimes", }, }, { $unwind: { path: "$doctorTimes", preserveNullAndEmptyArrays: false, }, }, { $match: { "doctorTimes.rxDuration": { $gt: 0 }, }, }, // 6. 计算统计信息 { $group: { _id: null, totalCount: { $sum: 1 }, avgDuration: { $avg: "$doctorTimes.rxDuration" }, minDuration: { $min: "$doctorTimes.rxDuration" }, maxDuration: { $max: "$doctorTimes.rxDuration" }, durations: { $push: "$doctorTimes.rxDuration" }, }, }, ]; const result = await db .collection("consult-order") .aggregate(pipeline) .toArray(); if (result.length === 0) { return { success: true, message: "查询成功", startDate: dayjs(startTime).format("YYYY-MM-DD HH:mm:ss"), endDate: dayjs(endTime).format("YYYY-MM-DD HH:mm:ss"), totalOrders: 0, stats: { totalCount: 0, avgDuration: 0, minDuration: 0, maxDuration: 0, durationRanges: [], }, }; } const stats = result[0]; const durations = stats.durations; // 按时长范围分组统计 const durationRanges = [ { range: "0-30s", min: 0, max: 30000, count: 0 }, { range: "30s-1分钟", min: 30000, max: 60000, count: 0 }, { range: "1分钟-2分钟", min: 60000, max: 120000, count: 0 }, { range: "2-3分钟", min: 120000, max: 180000, count: 0 }, { range: "3分钟以上", min: 180000, max: Infinity, count: 0 }, ]; durations.forEach((duration) => { const range = durationRanges.find( (r) => duration >= r.min && duration < r.max ); if (range) { range.count++; } }); return { success: true, message: "查询成功", startDate: dayjs(startTime).format("YYYY-MM-DD HH:mm:ss"), endDate: dayjs(endTime).format("YYYY-MM-DD HH:mm:ss"), totalOrders: stats.totalCount, stats: { totalCount: stats.totalCount, avgDuration: Math.round(stats.avgDuration), minDuration: stats.minDuration, maxDuration: stats.maxDuration, durationRanges: durationRanges.filter((range) => range.count > 0), }, }; } async function getDrugStats(params, db) { const { startTime, endTime } = params; const startTimeTemp = dayjs(startTime).startOf("day").valueOf(); const endTimeTemp = dayjs(endTime).endOf("day").valueOf(); // 先检查基础数据 const baseCount = await db.collection("consult-order").countDocuments({ age: { $gte: 60 }, createTime: { $gte: startTimeTemp, $lte: endTimeTemp }, }); console.log("基础订单数量 (age >= 60):", baseCount); const pipeline = [ { $match: { age: { $gte: 60 }, createTime: { $gte: startTimeTemp, $lte: endTimeTemp }, }, }, { $project: { orderId: 1, }, }, { $lookup: { from: "diagnostic-record", localField: "orderId", foreignField: "orderId", as: "diagnosticRecords", }, }, { $unwind: { path: "$diagnosticRecords", preserveNullAndEmptyArrays: false, }, }, { $match: { "diagnosticRecords.status": "PASS", }, }, // 调试:检查诊断记录数量 { $addFields: { debug_drugs_count: { $size: { $ifNull: ["$diagnosticRecords.drugs", []] }, }, }, }, { $unwind: { path: "$diagnosticRecords.drugs", preserveNullAndEmptyArrays: false, }, }, { $group: { _id: { drugName: "$diagnosticRecords.drugs.drugName", insuranceCode: "$diagnosticRecords.drugs.insurance_code", productId: "$diagnosticRecords.drugs.product_id", specification: "$diagnosticRecords.drugs.specification", unit: "$diagnosticRecords.drugs.unit", }, totalQuantity: { $sum: "$diagnosticRecords.drugs.quantity" }, prescriptionCount: { $sum: 1 }, drugInfo: { $first: "$diagnosticRecords.drugs" }, }, }, // 8. 格式化输出 { $project: { _id: 0, drugName: "$_id.drugName", insuranceCode: "$_id.insuranceCode", productId: "$_id.productId", specification: "$_id.specification", unit: "$_id.unit", totalQuantity: 1, prescriptionCount: 1, drugInfo: 1, }, }, // 9. 按总数量降序排序 { $sort: { totalQuantity: -1, }, }, // 10. 限制返回前30条记录 { $limit: 30, }, ]; const result = await db .collection("consult-order") .aggregate(pipeline) .toArray(); console.log("聚合结果数量:", result.length); console.log("前3条结果:", result.slice(0, 3)); // 计算总统计信息 const totalPrescriptions = result.reduce( (sum, item) => sum + item.prescriptionCount, 0 ); const totalQuantity = result.reduce( (sum, item) => sum + item.totalQuantity, 0 ); return { success: true, message: "查询成功", startDate: dayjs(startTime).format("YYYY-MM-DD HH:mm:ss"), endDate: dayjs(endTime).format("YYYY-MM-DD HH:mm:ss"), summary: { totalPrescriptions, // 总处方次数 totalDrugTypes: result.length, // 药品种类数(前30) totalQuantity, // 总药品数量(盒数) }, data: result, // Top30 药品统计,按数量降序排列 }; } async function createAlipayOrder() { try { const tradeApi = require("../trade"); const payload = { totalAmount: 0.02, businessNo: dayjs().valueOf().toString(), businessType: 'onlineMedicinePurchase', desc: `[${dayjs().format('YYYY-MM-DD HH:mm:ss')}] 线上购药订单测试`, openId: '2088612594454930' } const { success, message, tradeNo } = await tradeApi.getAlipayTradeNo(payload); if (success) { return { success: true, message: '创建交易订单成功', tradeNo: tradeNo }; } else { return { success: false, message: message }; } } catch (e) { return { success: false, message: e.message }; } } async function queryAlipayOrder(data) { try { const tradeApi = require("../trade"); const { success, message, data: tradeInfo } = await tradeApi.queryAlipayTradeNo(data); if (success) { return { success: true, message: '查询交易订单成功', data: tradeInfo }; } else { return { success: false, message: message }; } } catch (e) { return { success: false, message: e.message }; } } async function refundAlipayOrder(data) { try { const tradeApi = require("../trade"); console.log(`申请退费: ${data.tradeNo}`) const res = await tradeApi.refundTrade({ tradeNo: data.tradeNo, reason: `${dayjs().format('YYYY-MM-DD HH:mm:ss')} 用户申请退款` }); return res } catch (e) { return { success: false, message: e.message }; } } async function fengniaoTest(ctx) { try { const fengniaoApi = require("../fengniao"); const res = await fengniaoApi.fengniaoTest(ctx); return { success: true, data: res } } catch (e) { return { success: false, message: e.message } } } async function emsTest() { try { const emsApi = require("../ems-express"); const res = await emsApi.emsTest(); return { success: true, data: res } } catch (e) { return { success: false, message: e.message } } } async function hisTest() { const hisApi = require("../zyt-his"); const res = await hisApi({ type: "hisTest" }); return { success: true, data: res } } async function lackTest(item, db) { try { const lackApi = require("../lack-medicine-reg"); const res = await lackApi({ type: "lackTest" }, db); return { success: true, data: res } } catch (e) { return { success: false, message: e.message } } } async function wineTest(item, db) { try { const api = require("../chinese-medicine-order"); return await api.main({ type: "wineTest" }, db); } catch (e) { return { success: false, message: e.message } } } async function purchaseTest(item, db) { try { const purchaseApi = require("../medicine-purchase-order"); const res = await purchaseApi({ type: "purchaseTest" }, db); return { success: true, data: res } } catch (e) { return { success: false, message: e.message } } } async function testMedicineNotify(item, db) { try { const purchaseApi = require("../medicine-purchase-order"); return await purchaseApi({ type: "testMedicineNotify" }, db); } catch (e) { return { success: false, message: e.message } } } async function tradeTest(item, db) { try { const tradeApi = require("../trade"); const res = await tradeApi.tradeTest(); return { success: true, data: res } } catch (e) { return { success: false, message: e.message } } }