forked from Yara724/api
blame and claim refactored
This commit is contained in:
16
src/utils/public-id/counter.schema.ts
Normal file
16
src/utils/public-id/counter.schema.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||
|
||||
@Schema({ collection: "counters", versionKey: false })
|
||||
export class CounterModel {
|
||||
/**
|
||||
* Counter name (e.g. "requestPublicId")
|
||||
*/
|
||||
@Prop({ type: String, required: true })
|
||||
_id: string;
|
||||
|
||||
@Prop({ type: Number, required: true, default: 0 })
|
||||
seq: number;
|
||||
}
|
||||
|
||||
export const CounterSchema = SchemaFactory.createForClass(CounterModel);
|
||||
|
||||
16
src/utils/public-id/public-id.module.ts
Normal file
16
src/utils/public-id/public-id.module.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { MongooseModule } from "@nestjs/mongoose";
|
||||
import { CounterModel, CounterSchema } from "./counter.schema";
|
||||
import { PublicIdService } from "./public-id.service";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
MongooseModule.forFeature([
|
||||
{ name: CounterModel.name, schema: CounterSchema },
|
||||
]),
|
||||
],
|
||||
providers: [PublicIdService],
|
||||
exports: [PublicIdService],
|
||||
})
|
||||
export class PublicIdModule {}
|
||||
|
||||
55
src/utils/public-id/public-id.service.ts
Normal file
55
src/utils/public-id/public-id.service.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { InjectModel } from "@nestjs/mongoose";
|
||||
import { Model } from "mongoose";
|
||||
import { CounterModel } from "./counter.schema";
|
||||
|
||||
@Injectable()
|
||||
export class PublicIdService {
|
||||
private static readonly COUNTER_NAME = "requestPublicId";
|
||||
private static readonly DIGITS = 5;
|
||||
private static readonly BASE = 10 ** PublicIdService.DIGITS; // 100000
|
||||
|
||||
constructor(
|
||||
@InjectModel(CounterModel.name)
|
||||
private readonly counterModel: Model<CounterModel>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Generates a human-friendly id like:
|
||||
* A00001 ... A99999, B00000 ... Z99999, AA00000, AB00000, ...
|
||||
*
|
||||
* - Atomic uniqueness is guaranteed by MongoDB `$inc` on a single counter doc.
|
||||
* - A unique index on `publicId` in target collections provides extra safety.
|
||||
*/
|
||||
async generateRequestPublicId(): Promise<string> {
|
||||
const counter = await this.counterModel.findOneAndUpdate(
|
||||
{ _id: PublicIdService.COUNTER_NAME },
|
||||
{ $inc: { seq: 1 } },
|
||||
{ upsert: true, new: true },
|
||||
);
|
||||
|
||||
const seq = counter.seq; // starts at 1
|
||||
const zeroBased = seq - 1;
|
||||
const prefixIndex = Math.floor(zeroBased / PublicIdService.BASE);
|
||||
const digits = zeroBased % PublicIdService.BASE;
|
||||
|
||||
const prefix = this.indexToLetters(prefixIndex);
|
||||
const suffix = String(digits).padStart(PublicIdService.DIGITS, "0");
|
||||
return `${prefix}${suffix}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 0 -> A, 25 -> Z, 26 -> AA, 27 -> AB, ...
|
||||
*/
|
||||
private indexToLetters(index: number): string {
|
||||
let n = index;
|
||||
let result = "";
|
||||
do {
|
||||
const rem = n % 26;
|
||||
result = String.fromCharCode(65 + rem) + result;
|
||||
n = Math.floor(n / 26) - 1;
|
||||
} while (n >= 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user