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

Node.js Express+MongoDB同标题商品最低价判断异常求助

问题解决:同标题商品最低价判断逻辑错误(所有cheaper返回true)

核心问题分析

  • 你用Product.exists查询数据库中的totalPrice字段,但这个字段是运行时动态计算的(含配送费),并未存入MongoDB,数据库中根本不存在该字段,导致每次查询都返回null,进而cheaper被错误赋值为true。
  • 额外问题:循环中每个商品单独发起数据库查询,性能低下;且配送费和用户位置绑定,数据库中没有存储针对当前用户的配送费,直接查数据库无法得到正确结果。

解决方案

  1. 先批量获取所有商品的基础数据,按标题分组。
  2. 对每个标题下的所有商品,基于当前用户位置批量计算各自的totalPrice。
  3. 找出每个标题下的最低totalPrice,再遍历每个商品对比自身价格,设置cheaper值。

修改后的完整代码

exports.getProducts = async (req, res, next) => {
  const DEFAULT_QUANTITY = 1;

  function calculateTotalPrice(productPrice, deliveryCost, quantity) {
    return productPrice * quantity + deliveryCost;
  }

  try {
    const userEmail = req.user.email;
    const user = await User.findOne({ email: userEmail });
    const isFarmer = user.accessLevel === 'farmer';
    // 提前关联查询农民数据,减少重复查询
    const products = await Product.find({ status: 'Live' }).populate('farmer', 'farmer');

    // 按标题分组商品
    const productsByTitle = {};
    products.forEach(product => {
      if (!productsByTitle[product.title]) {
        productsByTitle[product.title] = [];
      }
      productsByTitle[product.title].push(product);
    });

    // 预计算每个标题下所有商品的最低totalPrice(仅非农民用户时执行)
    const titleMinTotalPrice = {};
    if (!isFarmer && user.customer?.slctdLocation) {
      const customerLocation = user.customer.slctdLocation;
      for (const title in productsByTitle) {
        const titleProducts = productsByTitle[title];
        // 批量计算该标题下所有商品的totalPrice
        const pricePromises = titleProducts.map(async product => {
          const farmer = product.farmer;
          const farmerSaleLocation = farmer.farmer.saleLocation;
          if (!farmerSaleLocation) return 0;

          try {
            const distanceResponse = await fetch(`https://maps.googleapis.com/maps/api/distancematrix/json?origins=${customerLocation.latitude},${customerLocation.longitude}&destinations=${farmerSaleLocation.latitude},${farmerSaleLocation.longitude}&key=${process.env.GOOGLE_MAPS_API_KEY}`);
            const distanceData = await distanceResponse.json();
            const distanceValue = distanceData.rows[0].elements[0].distance.text;
            const distanceNum = parseFloat(distanceValue.replace("Km", "").trim());
            const deliveryCost = distanceNum * farmer.farmer.deliveryCharge;
            return calculateTotalPrice(product.price, deliveryCost, DEFAULT_QUANTITY);
          } catch (err) {
            console.log(err);
            return 0;
          }
        });

        const totalPrices = await Promise.all(pricePromises);
        // 过滤无效价格后取最小值
        titleMinTotalPrice[title] = Math.min(...totalPrices.filter(p => p > 0));
      }
    }

    // 生成最终响应数据并设置cheaper值
    const productDetails = await Promise.all(products.map(async product => {
      const farmer = product.farmer;
      const farmerSaleLocation = farmer.farmer.saleLocation;
      const customerLocation = isFarmer ? null : user.customer?.slctdLocation;

      let deliveryCost = 0;
      let distanceValue = 0;
      let distanceNum = 0;
      let totalPrice = 0;
      const title = product.title;
      let cheaper = false;

      if (farmerSaleLocation && customerLocation) {
        try {
          const distanceResponse = await fetch(`https://maps.googleapis.com/maps/api/distancematrix/json?origins=${customerLocation.latitude},${customerLocation.longitude}&destinations=${farmerSaleLocation.latitude},${farmerSaleLocation.longitude}&key=${process.env.GOOGLE_MAPS_API_KEY}`);
          const distanceData = await distanceResponse.json();
          distanceValue = distanceData.rows[0].elements[0].distance.text;
          distanceNum = parseFloat(distanceValue.replace("Km", "").trim());
          deliveryCost = distanceNum * farmer.farmer.deliveryCharge;
          totalPrice = calculateTotalPrice(product.price, deliveryCost, DEFAULT_QUANTITY);

          // 对比当前商品价格与标题最低价格
          cheaper = totalPrice === titleMinTotalPrice[title];
          console.log(`Product name ${title} delivery Cost ${deliveryCost} is it cheaper : ${cheaper}`);
        } catch (err) {
          console.log(err);
        }
      }

      return {
        _id: product._id,
        price: product.price,
        title: title,
        imageUrl: product.imageUrls[0],
        overallRating: product.overallRating,
        unit: product.unit,
        likes: product.likes,
        deliveryCost: deliveryCost,
        distance: distanceNum,
        totalPrice: totalPrice,
        cheaper: cheaper // 新增返回cheaper字段
      };
    }));

    res.json(productDetails);
  } catch (error) {
    console.error(error);
    res.status(500).send('Server Error! something is wrong');
  }
};

额外优化说明

  • 使用populate('farmer', 'farmer')提前关联查询农民数据,避免循环中重复调用User.findById,提升性能。
  • 批量计算每个标题的最低价格,减少重复的Google Maps API调用(可进一步添加距离缓存优化)。
  • 处理了customerLocation不存在的边界情况。

内容的提问来源于stack exchange,提问作者Nadun Rupasinghe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 08:25:35