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

如何在MongoDB pre-save钩子中自动设置操作用户等额外信息

问题解答

首先明确:可以向pre-save方法传递自定义参数,针对你的场景有两种常用实现方案:

方案1:手动传参(适合少部分表需要填充操作人场景)

Mongoose 提供了$locals属性专门用于在实体实例和钩子之间传递自定义数据,这部分数据不会被持久化到数据库,也不会和Schema定义的字段冲突。

改造步骤

  1. 调用save方法前,将当前操作用户ID写入实体的$locals属性:
// 示例:接口中拿到登录用户ID后实例化实体
const newRole = new Role({ name: '运营' });
newRole.$locals.operatorId = loginUser._id; // loginUser是当前登录的用户对象
await newRole.save();
  1. 修改pre-save钩子,读取$locals中的操作用户ID:
RoleSchema.pre("save", async function (done) {
  const operatorId = this.$locals.operatorId;
  // 可选校验:避免遗漏传入操作用户ID
  if (!operatorId) return done(new Error('操作人ID不能为空'));

  if(!this.isNew){
    this.set('modified_at', new Date());
    this.set('modified_by', operatorId);
  } else {
    this.set('created_at', new Date());
    this.set('created_by', operatorId);
  }
  done();
});

方案2:自动注入(适合全表通用审计字段场景)

如果你的项目是Express/Koa类的后端服务,大部分表都需要存创建人、修改人信息,可以通过Mongoose插件 + 请求上下文的方式实现全自动填充,不需要业务代码手动传参。

实现逻辑

  1. 用Node.js的async_hooks封装请求上下文存储,每个请求的登录用户信息存在上下文里。
  2. 写一个通用的审计插件,给所有需要的Schema注册:
const mongoose = require('mongoose');
// 假设你已经封装好了从请求上下文取登录用户ID的方法getLoginUserId
const { getLoginUserId } = require('../utils/request-context');

const auditPlugin = (schema) => {
  // 自动给Schema添加审计字段,不用每个Schema重复定义
  schema.add({
    created_at: Date,
    created_by: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
    modified_at: Date,
    modified_by: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
  });

  schema.pre('save', function (done) {
    const operatorId = getLoginUserId();
    if (!operatorId) return done(new Error('未获取到登录用户信息'));
    
    if(!this.isNew){
      this.modified_at = new Date();
      this.modified_by = operatorId;
    } else {
      this.created_at = new Date();
      this.created_by = operatorId;
    }
    done();
  });

  // 可选:适配update系列方法的审计字段填充,save钩子不会触发update类操作
  schema.pre(['updateOne', 'findOneAndUpdate'], function (done) {
    const operatorId = getLoginUserId();
    this.set({ modified_at: new Date(), modified_by: operatorId });
    done();
  });
};

// 给需要的Schema注册插件
RoleSchema.plugin(auditPlugin);

注意事项

  • 不要直接给实体实例挂载自定义属性传参,容易和Schema字段冲突甚至被意外存入数据库,优先用官方提供的$locals。
  • 如果用到updateOne、findOneAndUpdate等非save的更新方法,需要单独添加对应pre钩子,逻辑和save钩子类似。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 10:15:03