解决MongoDB E11000重复键错误及购物车数据存取问题
解决E11000重复键错误&实现购物车功能方案
一、先修复当前的重复键错误
错误提示E11000 duplicate key error collection: test.products index: name_1 dup key: { name: "tomek" },但你的Product模型中name字段并未设置unique: true,说明数据库中已存在针对name字段的唯一索引,需要删除它:
- 打开MongoDB Shell,连接到目标数据库:
mongosh use test - 查看集合的所有索引:
db.products.getIndexes() - 删除
name_1这个唯一索引:db.products.dropIndex("name_1")
二、重构购物车的正确实现
当前模型设计不符合购物车逻辑(同一个用户重复添加同一件商品应更新数量,而非创建新条目),需调整模型和接口逻辑:
1. 创建购物车条目模型(models/CartItem.js)
import mongoose from "mongoose"; const { Schema } = mongoose; const cartItemSchema = new Schema({ // 用户唯一标识(已登录用户email唯一) userEmail: { type: String, required: true, }, // 商品本身的唯一ID(确保同一件商品ID一致) productId: { type: String, required: true, }, // 商品基础信息(直接存储方便前端展示) productName: { type: String, required: true, }, category: { type: String, required: true, }, title: { type: String, required: true, }, price: { type: String, required: true, }, // 商品数量 quantity: { type: Number, default: 1, min: 1, }, }, { timestamps: true }); // 联合唯一索引:同一个用户的同一件商品仅存一条记录 cartItemSchema.index({ userEmail: 1, productId: 1 }, { unique: true }); export default mongoose.models.CartItem || mongoose.model("CartItem", cartItemSchema);
2. 修改购物车添加接口(api/cart/route.js)
替换原api/products/route.js,或新建购物车专用接口:
import { NextResponse } from "next/server"; import CartItem from "@/models/CartItem"; import connect from "@/utils/db"; export const POST = async (request) => { const { userEmail, productId, productName, category, title, price } = await request.json(); await connect(); try { // 存在则数量+1,不存在则创建新条目 await CartItem.findOneAndUpdate( { userEmail, productId }, { $inc: { quantity: 1 }, $setOnInsert: { productName, category, title, price } }, { upsert: true, new: true } ); return new NextResponse("商品已添加到购物车", { status: 200 }); } catch (err) { return new NextResponse("添加失败:" + err.message, { status: 500 }); } }; // 新增获取用户购物车列表的GET接口 export const GET = async (request) => { const { searchParams } = new URL(request.url); const userEmail = searchParams.get("userEmail"); if (!userEmail) { return new NextResponse("缺少用户邮箱", { status: 400 }); } await connect(); try { const cartItems = await CartItem.find({ userEmail }); return new NextResponse(JSON.stringify(cartItems), { status: 200 }); } catch (err) { return new NextResponse("获取购物车失败:" + err.message, { status: 500 }); } };
3. 调整前端请求代码
注意:productId用商品本身的唯一ID,而非随机生成的ID:
// 假设current[0]是选中的商品,自带唯一id字段 const productId = current[0].id; const userEmail = session.data.user.email; const productName = current[0].title; const category = current[0].category; const title = current[0].title; const price = current[0].price; try { const res = await fetch("/api/cart", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ userEmail, productId, productName, category, title, price, }), }); if (res.ok) { console.log("商品已加入购物车"); // 可在此处触发购物车列表刷新 } else { const errMsg = await res.text(); console.log("添加失败:", errMsg); } } catch (err) { console.log("请求出错:", err); }
4. 前端获取购物车列表代码
async function fetchCart() { const userEmail = session.data.user.email; try { const res = await fetch(`/api/cart?userEmail=${encodeURIComponent(userEmail)}`); if (res.ok) { const cartItems = await res.json(); console.log("购物车列表:", cartItems); // 渲染购物车组件 } else { console.log("获取购物车失败"); } } catch (err) { console.log("请求出错:", err); } }
三、额外说明
- 原
Product模型应用于存储商品目录,购物车需单独模型管理用户与商品的关联关系 - 使用
findOneAndUpdate的upsert选项可避免重复创建条目,自动处理数量更新 - 联合唯一索引
{userEmail:1, productId:1}确保同一个用户的同一件商品不会重复存储
内容的提问来源于stack exchange,提问作者tomaszM-dev
相关产品推荐
相关产品推荐

