forked from Yara724/api
155 lines
4.6 KiB
TypeScript
155 lines
4.6 KiB
TypeScript
import { Injectable, Logger, OnModuleInit } from "@nestjs/common";
|
|
import { InjectModel } from "@nestjs/mongoose";
|
|
import { Model } from "mongoose";
|
|
import {
|
|
resolveFanavaranClientKey,
|
|
type FanavaranClientKey,
|
|
} from "src/core/config/fanavaran-client.config";
|
|
import { SystemSettingsService } from "src/system-settings/system-settings.service";
|
|
import { OFFLINE_INQUIRY_SEEDS } from "./offline-inquiry.seeds";
|
|
import {
|
|
OfflineInquiry,
|
|
OfflineInquiryDocument,
|
|
} from "./schema/offline-inquiry.schema";
|
|
|
|
export interface OfflinePlateLookupInput {
|
|
nationalCode: string;
|
|
leftDigits: string;
|
|
centerAlphabet: string;
|
|
centerDigits: string;
|
|
ir: string;
|
|
clientKey?: FanavaranClientKey;
|
|
}
|
|
|
|
export interface OfflinePlateInquiryHit {
|
|
raw: Record<string, unknown>;
|
|
mapped: Record<string, unknown>;
|
|
source: string;
|
|
fanavaranDriverId?: number;
|
|
insurance?: Record<string, unknown>;
|
|
person?: Record<string, unknown>;
|
|
plateId?: string;
|
|
label?: string;
|
|
}
|
|
|
|
@Injectable()
|
|
export class OfflineInquiryService implements OnModuleInit {
|
|
private readonly logger = new Logger(OfflineInquiryService.name);
|
|
|
|
constructor(
|
|
@InjectModel(OfflineInquiry.name)
|
|
private readonly offlineInquiryModel: Model<OfflineInquiryDocument>,
|
|
private readonly systemSettingsService: SystemSettingsService,
|
|
) {}
|
|
|
|
async onModuleInit(): Promise<void> {
|
|
await this.seedMissing();
|
|
}
|
|
|
|
/** Normalize Persian plate letter (strip ZWNJ / Arabic variants). */
|
|
static normalizePlateAlphabet(value: string): string {
|
|
return String(value ?? "")
|
|
.replace(/[\u200c\u200d\uFEFF]/g, "")
|
|
.replace(/ي/g, "ی")
|
|
.replace(/ك/g, "ک")
|
|
.trim();
|
|
}
|
|
|
|
static normalizeDigits(value: string | number): string {
|
|
return String(value ?? "").trim();
|
|
}
|
|
|
|
async seedMissing(): Promise<void> {
|
|
for (const seed of OFFLINE_INQUIRY_SEEDS) {
|
|
const plate = {
|
|
leftDigits: OfflineInquiryService.normalizeDigits(seed.plate.leftDigits),
|
|
centerAlphabet: OfflineInquiryService.normalizePlateAlphabet(
|
|
seed.plate.centerAlphabet,
|
|
),
|
|
centerDigits: OfflineInquiryService.normalizeDigits(
|
|
seed.plate.centerDigits,
|
|
),
|
|
ir: OfflineInquiryService.normalizeDigits(seed.plate.ir),
|
|
};
|
|
const existing = await this.offlineInquiryModel
|
|
.findOne({
|
|
clientKey: seed.clientKey,
|
|
inquiryType: seed.inquiryType,
|
|
nationalCode: seed.nationalCode,
|
|
"plate.leftDigits": plate.leftDigits,
|
|
"plate.centerAlphabet": plate.centerAlphabet,
|
|
"plate.centerDigits": plate.centerDigits,
|
|
"plate.ir": plate.ir,
|
|
})
|
|
.select({ _id: 1 })
|
|
.lean()
|
|
.exec();
|
|
if (existing) {
|
|
continue;
|
|
}
|
|
await this.offlineInquiryModel.create({
|
|
...seed,
|
|
plate,
|
|
enabled: true,
|
|
});
|
|
this.logger.log(
|
|
`Seeded offlineInquiry ${seed.clientKey} ${seed.nationalCode} plate=${seed.plateId}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
async findPlateInquiry(
|
|
input: OfflinePlateLookupInput,
|
|
): Promise<OfflinePlateInquiryHit | null> {
|
|
if (!(await this.systemSettingsService.isOfflineInquiryEnabled())) {
|
|
return null;
|
|
}
|
|
|
|
const clientKey = input.clientKey ?? resolveFanavaranClientKey();
|
|
const nationalCode = OfflineInquiryService.normalizeDigits(
|
|
input.nationalCode,
|
|
);
|
|
const leftDigits = OfflineInquiryService.normalizeDigits(input.leftDigits);
|
|
const centerAlphabet = OfflineInquiryService.normalizePlateAlphabet(
|
|
input.centerAlphabet,
|
|
);
|
|
const centerDigits = OfflineInquiryService.normalizeDigits(
|
|
input.centerDigits,
|
|
);
|
|
const ir = OfflineInquiryService.normalizeDigits(input.ir);
|
|
|
|
const doc = await this.offlineInquiryModel
|
|
.findOne({
|
|
clientKey,
|
|
inquiryType: "thirdPartyPlate",
|
|
nationalCode,
|
|
enabled: true,
|
|
"plate.leftDigits": leftDigits,
|
|
"plate.centerAlphabet": centerAlphabet,
|
|
"plate.centerDigits": centerDigits,
|
|
"plate.ir": ir,
|
|
})
|
|
.lean()
|
|
.exec();
|
|
|
|
if (!doc?.inquiry?.raw || !doc?.inquiry?.mapped) {
|
|
return null;
|
|
}
|
|
|
|
this.logger.log(
|
|
`[OFFLINE] Hit plate inquiry client=${clientKey} nationalCode=${nationalCode} plate=${doc.plateId ?? `${ir}-${leftDigits}-${centerAlphabet}-${centerDigits}`}`,
|
|
);
|
|
|
|
return {
|
|
raw: doc.inquiry.raw as Record<string, unknown>,
|
|
mapped: doc.inquiry.mapped as Record<string, unknown>,
|
|
source: doc.inquiry.source || "OFFLINE_SEED_INQUIRY",
|
|
fanavaranDriverId: doc.fanavaranDriverId,
|
|
insurance: doc.insurance,
|
|
person: doc.person,
|
|
plateId: doc.plateId,
|
|
label: doc.label,
|
|
};
|
|
}
|
|
}
|