Node.js Express+MongoDB同标题商品最低价判断异常求助
问题解决:同标题商品最低价判断逻辑错误(所有cheaper返回true)
核心问题分析
- 你用
Product.exists查询数据库中的totalPrice字段,但这个字段是运行时动态计算的(含配送费),并未存入MongoDB,数据库中根本不存在该字段,导致每次查询都返回null,进而cheaper被错误赋值为true。 - 额外问题:循环中每个商品单独发起数据库查询,性能低下;且配送费和用户位置绑定,数据库中没有存储针对当前用户的配送费,直接查数据库无法得到正确结果。
解决方案
- 先批量获取所有商品的基础数据,按标题分组。
- 对每个标题下的所有商品,基于当前用户位置批量计算各自的
totalPrice。 - 找出每个标题下的最低
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
相关产品推荐
相关产品推荐

