You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mongoose按ID统计分销套餐数据:解决查询重复执行错误及金额统计

问题描述

Node.js项目中提供多种订阅套餐,新用户通过老用户链接购买时,老用户可获福利,相关数据存在affiliation表。需求是给老用户展示其推荐用户中,各套餐的购买人数及对应总金额。

现有代码使用Mongoose的distinct时出现错误:Query was already executed before,虽能输出package_id结果,但无法正确关联套餐金额。

尝试的服务层代码:

exports.getTotalReferral = async (user_id, start_date, end_date) => {
  let total_affiliation_amount = 0;
  let total_affiliation_record = 0;

  start_date = dayjs(start_date).utc(true).format();
  end_date = dayjs(end_date).endOf('day').utc(true).format();

  let data = await Affiliation.find(
    {
      affiliated_by: user_id,
      created_at: {
        $gte: start_date,
        $lte: end_date
      }
    },
  ).distinct('package_id', function (err, result) {
    console.log(result);  // this logs the result of the package id
  });

  return { data, total_affiliation_amount, total_affiliation_record };
}

控制器代码:

exports.totalReferral = async (req, res) => {
  try {
    const total_referral = await affiliationService.getTotalReferral(req.data.id, req.body.start_date, req.body.end_date);
    return res.succeed(total_referral, "success");

  } catch (error) {
    return res.status(404).json({ message: error.message });
  };
};
解决方案

1. 修复Query was already executed before错误

错误原因:同时使用await和回调函数处理distinct查询,Mongoose查询对象只能执行一次,await已触发查询执行,回调会再次触发执行,导致重复执行报错。

解决方法:移除回调函数,直接通过await获取distinct结果:

// 替换原查询代码
const packageIds = await Affiliation.find(
  {
    affiliated_by: user_id,
    created_at: { $gte: start_date, $lte: end_date }
  }
).distinct('package_id');
console.log(packageIds); // 直接拿到去重后的package_id数组

2. 实现套餐购买人数+金额统计

要同时统计各套餐的购买人数和总金额,使用Mongoose的聚合查询更高效,无需先查distinct再循环统计,一次聚合即可完成:

情况1:Affiliation表直接存储套餐金额

直接用$group分组聚合:

exports.getTotalReferral = async (user_id, start_date, end_date) => {
  start_date = dayjs(start_date).utc(true).toDate(); // 转成Date对象更稳妥
  end_date = dayjs(end_date).endOf('day').utc(true).toDate();

  const stats = await Affiliation.aggregate([
    // 过滤推荐人ID和时间范围
    {
      $match: {
        affiliated_by: user_id,
        created_at: { $gte: start_date, $lte: end_date }
      }
    },
    // 按套餐ID分组,统计人数和总金额
    {
      $group: {
        _id: "$package_id", // 分组键:套餐ID
        count: { $sum: 1 }, // 该套餐购买人数
        totalAmount: { $sum: "$amount" } // 该套餐总金额
      }
    },
    // 重命名字段,让返回结果更直观
    {
      $project: {
        package_id: "$_id",
        count: 1,
        totalAmount: 1,
        _id: 0
      }
    }
  ]);

  // 计算所有推荐记录的总人数和总金额
  const total_affiliation_record = stats.reduce((sum, item) => sum + item.count, 0);
  const total_affiliation_amount = stats.reduce((sum, item) => sum + item.totalAmount, 0);

  return { data: stats, total_affiliation_amount, total_affiliation_record };
}

情况2:套餐金额存储在Package表,需要关联查询

用$lookup关联Package表获取金额后再聚合:

exports.getTotalReferral = async (user_id, start_date, end_date) => {
  start_date = dayjs(start_date).utc(true).toDate();
  end_date = dayjs(end_date).endOf('day').utc(true).toDate();

  const stats = await Affiliation.aggregate([
    { $match: { affiliated_by: user_id, created_at: { $gte: start_date, $lte: end_date } } },
    // 关联Package表获取套餐信息
    {
      $lookup: {
        from: "packages", // Package表的集合名称
        localField: "package_id",
        foreignField: "_id",
        as: "package"
      }
    },
    // 展开关联的套餐数组(每个关联仅一条数据)
    { $unwind: "$package" },
    // 按套餐ID分组统计
    {
      $group: {
        _id: "$package_id",
        count: { $sum: 1 },
        totalAmount: { $sum: "$package.price" }, // 取Package表中的价格字段
        packageName: { $first: "$package.name" } // 可选:返回套餐名称
      }
    },
    {
      $project: {
        package_id: "$_id",
        packageName: 1,
        count: 1,
        totalAmount: 1,
        _id: 0
      }
    }
  ]);

  const total_affiliation_record = stats.reduce((sum, item) => sum + item.count, 0);
  const total_affiliation_amount = stats.reduce((sum, item) => sum + item.totalAmount, 0);

  return { data: stats, total_affiliation_amount, total_affiliation_record };
}

3. 返回结果示例

控制器调用后返回的结果格式如下:

{
  "data": [
    {
      "package_id": "60d21b4667d0d8992e610c85",
      "count": 3,
      "totalAmount": 30
    },
    {
      "package_id": "60d21b8967d0d8992e610c86",
      "count": 1,
      "totalAmount": 0
    }
  ],
  "total_affiliation_amount": 30,
  "total_affiliation_record": 4
}

内容的提问来源于stack exchange,提问作者John Oliver

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 18:39:20