2026-07-27 11:28:33 +08:00

911 lines
25 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 }
}
}