blame and claim refactored

This commit is contained in:
2026-02-24 12:19:25 +03:30
parent 0d8858f458
commit 35732dd70a
81 changed files with 11603 additions and 77 deletions

View 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);

View 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 {}

View 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;
}
}