如何在MERN栈中实现ticketno字段自动生成递增序列号?
Mongoose实现ticketno字段自动递增序列号方案
方案1:使用独立计数器集合(推荐,并发安全)
这种方式通过维护专门存储序列值的集合,利用MongoDB的原子操作findOneAndUpdate保证不会出现重复编号,无并发冲突问题。
步骤1:创建计数器Schema和模型
新建Counter的Schema,用于存储每个业务表的当前最大序列号:
const counterSchema = new mongoose.Schema({ _id: { type: String, required: true }, sequenceValue: { type: Number, default: 0 } }); const Counter = mongoose.model('Counter', counterSchema);
步骤2:初始化计数器
首次运行前初始化,也可添加逻辑自动判断初始化:
// 初始化kamForm表的ticketno计数器,起始值可自定义,比如从1000开始就设为1000 Counter.findOneAndUpdate( { _id: 'kamFormTicketNo' }, { $setOnInsert: { sequenceValue: 0 } }, { upsert: true, new: true } ).exec();
步骤3:给原有kamFormSchema添加pre save中间件
在原有kamFormSchema定义的位置,添加保存前自动生成ticketno的逻辑:
kamFormSchema.pre('save', async function(next) { // 仅新创建的工单生成ticketno,修改操作不改动原有编号 if (!this.isNew) return next(); try { // 原子操作获取下一个序列号,返回更新后的值 const counter = await Counter.findOneAndUpdate( { _id: 'kamFormTicketNo' }, { $inc: { sequenceValue: 1 } }, { new: true, upsert: true } ); // 可根据需求格式化ticketno,比如补零、加前缀,示例:'TKT-00001' this.ticketno = counter.sequenceValue.toString(); // 固定长度补零写法参考 // this.ticketno = `TKT${counter.sequenceValue.toString().padStart(5, '0')}`; next(); } catch (err) { next(err); } });
方案2:查询当前最大编号+1(轻量场景用,不适合高并发)
如果业务量小,并发创建工单概率极低,可使用更简单的方案,直接查询现有最大ticketno加1:
kamFormSchema.pre('save', async function(next) { if (!this.isNew) return next(); try { // 按ticketno倒序取第一条得到最大值 const latestForm = await this.constructor.findOne().sort({ ticketno: -1 }).exec(); const maxNo = latestForm ? parseInt(latestForm.ticketno) : 0; this.ticketno = (maxNo + 1).toString(); next(); } catch (err) { next(err); } });
注意事项
- 如果需要ticketno带业务前缀、固定位数,直接在赋值的时候拼接格式化即可
- 高并发场景必须使用方案1,方案2在同时多个请求创建工单时会出现重复编号问题
- 已经有历史数据的情况下,初始化计数器时要把sequenceValue设置为现有最大的ticketno数值,避免和历史编号重复
内容的提问来源于stack exchange,提问作者stmp_lee
相关产品推荐
相关产品推荐

