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

MongoDB中基于邮箱字段将用户信息关联至优惠券集合

基于邮箱关联用户与优惠券的实现方案

需求背景

现有MongoDB的Coupons和Users集合,原逻辑通过存储用户ID并使用populate实现关联,现需改为基于邮箱字段(Coupons.email匹配Users.user_email)关联,且创建优惠券时需将对应用户的完整信息关联存储到Coupons集合中。


方案一:嵌入完整用户信息到优惠券文档

该方案在创建优惠券时,根据传入的邮箱查询用户完整信息,直接嵌入到优惠券文档中,无需后续关联查询。

1. 修改Coupon集合Schema

const mongoose = require("mongoose");

const couponSchema = new mongoose.Schema({
  // 替换原user字段为userInfo,用于存储完整用户信息
  userInfo: {
    type: Object, // 若需要严格结构,可复制UserSchema的定义,此处用Object更灵活
    required: true
  },
  email: {
    type: String,
    required: true,
    lowercase: true // 统一小写,避免大小写不匹配
  },
  couponCode: {
    type: String,
    required: true,
    unique: true,
  },
  amount: {
    type: Number,
    required: true,
  },
  expireDate: {
    type: String,
    required: true,
    default: "",
  },
  isActive: {
    type: Boolean,
    required: true,
    default: true,
  },
});

const Coupon = mongoose.model("Coupon", couponSchema);

module.exports = Coupon;

2. 修改创建优惠券的控制器代码

const User = require('./path-to-user-model'); // 引入User模型
const Coupon = require('./path-to-coupon-model');

const createCoupon = async (req, res) => {
  const { email, amount, expireDate, isActive } = req.body;
  // 移除原user参数,改为通过email查询用户

  function couponGenerator() {
    var coupon = "";
    var possible = "abcdefghijklmnopqrstuvwxyz0123456789";
    for (var i = 0; i < 6; i++) {
      coupon += possible.charAt(Math.floor(Math.random() * possible.length));
    }
    return coupon.toUpperCase();
  }
  const couponCode = couponGenerator();

  try {
    // 第一步:根据邮箱查询用户,统一转为小写匹配
    const targetUser = await User.findOne({ user_email: email.toLowerCase() });
    if (!targetUser) {
      return res.json({
        status: "ERROR",
        error: "用户不存在"
      });
    }

    // 第二步:创建优惠券,嵌入完整用户信息
    const newCoupon = new Coupon({
      userInfo: targetUser.toObject(), // 转为普通对象存入
      email: email.toLowerCase(),
      couponCode,
      amount,
      expireDate,
      isActive,
    });

    await newCoupon.save();

    res.json({
      status: "SUCCESS",
      Coupon: newCoupon,
    });
  } catch (error) {
    res.json({
      status: "ERROR",
      error: error.message
    });
  }
};

方案二:基于邮箱字段实现引用关联(保留populate能力)

若需保持集合间的引用关系而非嵌入,可通过mongoose虚拟关联实现基于邮箱的populate查询。

1. 修改Coupon集合Schema

const mongoose = require("mongoose");

const couponSchema = new mongoose.Schema({
  email: {
    type: String,
    required: true,
    lowercase: true
  },
  couponCode: {
    type: String,
    required: true,
    unique: true,
  },
  amount: {
    type: Number,
    required: true,
  },
  expireDate: {
    type: String,
    required: true,
    default: "",
  },
  isActive: {
    type: Boolean,
    required: true,
    default: true,
  },
});

// 定义虚拟关联,通过email匹配User的user_email
couponSchema.virtual('user', {
  ref: 'User',
  localField: 'email',
  foreignField: 'user_email',
  justOne: true // 因为user_email是唯一的,所以只返回一个用户
});

// 确保虚拟字段在转换为JSON/Object时被包含
couponSchema.set('toObject', { virtuals: true });
couponSchema.set('toJSON', { virtuals: true });

const Coupon = mongoose.model("Coupon", couponSchema);

module.exports = Coupon;

2. 修改创建优惠券的控制器代码

const Coupon = require('./path-to-coupon-model');

const createCoupon = async (req, res) => {
  const { email, amount, expireDate, isActive } = req.body;

  function couponGenerator() {
    var coupon = "";
    var possible = "abcdefghijklmnopqrstuvwxyz0123456789";
    for (var i = 0; i < 6; i++) {
      coupon += possible.charAt(Math.floor(Math.random() * possible.length));
    }
    return coupon.toUpperCase();
  }
  const couponCode = couponGenerator();

  try {
    const newCoupon = new Coupon({
      email: email.toLowerCase(),
      couponCode,
      amount,
      expireDate,
      isActive,
    });

    await newCoupon.save();

    res.json({
      status: "SUCCESS",
      Coupon: newCoupon,
    });
  } catch (error) {
    res.json({
      status: "ERROR",
      error: error.message
    });
  }
};

3. 查询优惠券时关联用户信息

查询时使用populate获取关联的用户数据:

const getCouponWithUser = async (req, res) => {
  try {
    const coupon = await Coupon.findOne({ couponCode: req.params.code }).populate('user');
    res.json({
      status: "SUCCESS",
      data: coupon
    });
  } catch (error) {
    res.json({
      status: "ERROR",
      error: error.message
    });
  }
};

注意事项

  • 统一邮箱大小写:在存储和查询时都转为小写,避免因大小写差异导致匹配失败
  • 用户存在校验:创建优惠券前务必校验邮箱对应的用户是否存在,避免无效数据
  • 方案选择:若用户信息变更不频繁,优先选嵌入方案(查询更快);若用户信息需实时同步,选引用关联方案

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 01:15:09