MongoDB统计实现:计算待支付总金额与uploadedAt到paidAt平均间隔
解决方案
方案1:使用MongoDB聚合查询(推荐,数据量大时性能更好)
你之前的聚合查询缺少两个核心部分:过滤条件和待支付金额统计,调整后的代码如下:
首先要把findAll里的过滤规则同步到聚合的$match阶段,保证和原有查询权限、软删除规则一致:
// 先拼接过滤条件,和findAll逻辑对齐 const baseCriteria = { company: company._id, deleted: { $ne: true } }; // 拼接权限过滤条件(如果有ability实例的话) let matchCriteria = baseCriteria; if (ability) { ability.throwUnlessCan('read', this.invoiceModel.modelName); matchCriteria = { ...toMongoQuery(ability, this.invoiceModel.modelName), ...matchCriteria }; } const stats = await this.invoiceModel.aggregate([ // 第一步:过滤符合条件的发票,和原有查询逻辑一致 { $match: matchCriteria }, // 第二步:分组统计,_id设为null表示统计所有符合条件的记录 { $group: { _id: null, // 统计待支付总金额:单条预期金额-已付金额,再求和 totalOutstanding: { $sum: { $subtract: ["$amount.expected", "$amount.paid"] } }, // 统计平均支付间隔,仅计算已付款(paidAt不为空)的记录 averagePaymentIntervalMs: { $avg: { $cond: { if: { $ne: ["$paidAt", null] }, then: { $subtract: ["$paidAt", "$uploadedAt"] }, else: "$$REMOVE" // 未付款的记录不参与平均计算 } } }, // 可选:统计总发票数、已付款发票数方便校验结果 totalInvoices: { $sum: 1 }, paidInvoices: { $sum: { $cond: [{ $ne: ["$paidAt", null] }, 1, 0] } } } } ]); // 处理返回结果 const result = stats[0] || { totalOutstanding: 0, averagePaymentIntervalMs: 0, totalInvoices: 0, paidInvoices: 0 }; // 可按需转换毫秒为天/小时,比如转天数: const averagePaymentDays = result.averagePaymentIntervalMs / (1000 * 60 * 60 * 24);
如果你需要按账户分组统计,只需要把$group的_id改为_id: "$account"即可。
方案2:基于现有findAll查询结果计算(适合数据量小的场景)
如果你的发票数据量不大,也可以直接复用已有的findAll方法,拿到结果后在应用层计算:
const invoiceParams = new FindParams(); invoiceParams.criteria = { company: company._id }; // 只查需要的字段,减少不必要的数据传输 invoiceParams.select = 'amount paidAt uploadedAt'; const invoices = await this.findAll(invoiceParams); let totalOutstanding = 0; let totalIntervalMs = 0; let paidCount = 0; for (const invoice of invoices) { // 累加待支付金额 totalOutstanding += invoice.amount.expected - invoice.amount.paid; // 计算已付款记录的日期间隔 if (invoice.paidAt) { totalIntervalMs += new Date(invoice.paidAt).getTime() - new Date(invoice.uploadedAt).getTime(); paidCount++; } } const averagePaymentIntervalMs = paidCount > 0 ? totalIntervalMs / paidCount : 0; const averagePaymentDays = averagePaymentIntervalMs / (1000 * 60 * 60 * 24);
注意事项
- 你的示例数据里
paidAt字段少写了一个闭合引号,实际业务代码中要避免这类语法错误 - 如果存在未付款的发票,记得在计算平均间隔时排除这类记录,避免统计结果失真
- 若金额字段存在小数,可根据业务需求添加
$round做精度处理
内容的提问来源于stack exchange,提问作者CashServices
相关产品推荐
相关产品推荐

