feat(api): identify-but-no-catalog fallback instead of a bare "unsupported"
When no catalog source can decode a VIN, the decode dead-ended with "Şase
numarası tanınamadı. Marka desteklenmiyor." even for valid, recognizable cars.
Now, before that error, run a best-effort identification (offline Corgi WMI +
NHTSA); if it yields a brand/model/year, tell the dealer what the car is and that
the request was logged ("Bu araç X olarak tanındı, ancak ... katalog henüz yok.
Talebiniz kaydedildi."). Logged distinctly (identified_no_catalog) so the coverage
backlog can be mined. Skipped on budget-abort.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -121,8 +121,32 @@ export class VehiclesService {
|
||||
const resolved = await this.resolveVin(vin, pcatCarId, emexCarIndex, userId, ctx);
|
||||
|
||||
if (!resolved) {
|
||||
const finalSource = ctx.timings.aborted ? "aborted" : "none";
|
||||
const errMsg = ctx.timings.aborted
|
||||
const aborted = !!ctx.timings.aborted;
|
||||
// No catalog source has this VIN. Best-effort identification (offline Corgi
|
||||
// WMI + NHTSA) so we don't dead-end with a bare "unsupported" — at least tell
|
||||
// the dealer what car it is. The miss is logged (with wmi) for the coverage
|
||||
// backlog either way. Skip on budget-abort (already a timeout, don't pile on).
|
||||
if (!aborted) {
|
||||
const basic = await this.identifyBasic(vin);
|
||||
if (basic) {
|
||||
ctx.timings.identified_no_catalog = 1;
|
||||
await this.logQuery(
|
||||
userId,
|
||||
vin,
|
||||
null,
|
||||
"none",
|
||||
false,
|
||||
Date.now() - startTime,
|
||||
`No catalog — identified as ${basic}`,
|
||||
ctx.timings,
|
||||
);
|
||||
throw new BadRequestException(
|
||||
`Bu araç ${basic} olarak tanındı, ancak bu şase için parça kataloğu henüz mevcut değil. Talebiniz kaydedildi.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
const finalSource = aborted ? "aborted" : "none";
|
||||
const errMsg = aborted
|
||||
? `Decode budget exceeded (${VehiclesService.RESOLVE_BUDGET_MS}ms)`
|
||||
: "Unknown VIN/brand";
|
||||
await this.logQuery(
|
||||
@@ -500,6 +524,31 @@ export class VehiclesService {
|
||||
return "timeout";
|
||||
}
|
||||
|
||||
/**
|
||||
* Best-effort identification for VINs no catalog could decode: offline Corgi WMI
|
||||
* → brand, NHTSA → model/year. Returns a display string ("Renault Clio 2018") or
|
||||
* null if even the brand is unknown. Only used to give the user a meaningful
|
||||
* "identified but no catalog yet" message instead of a bare "unsupported".
|
||||
*/
|
||||
private async identifyBasic(vin: string): Promise<string | null> {
|
||||
const corgi = this.corgiService.decodeVin(vin);
|
||||
let brand: string | null = corgi?.isKnown ? corgi.brandName : null;
|
||||
let model: string | null = null;
|
||||
let year: string | number | null = corgi?.modelYear ?? null;
|
||||
try {
|
||||
const nhtsa = await this.vinApiService.decodeVin(vin);
|
||||
if (nhtsa) {
|
||||
brand = brand || nhtsa.make || null;
|
||||
model = nhtsa.model || null;
|
||||
year = year || nhtsa.modelYear || null;
|
||||
}
|
||||
} catch {
|
||||
// NHTSA is best-effort; a brand from Corgi alone is still useful.
|
||||
}
|
||||
if (!brand && !model) return null;
|
||||
return [brand, model, year].filter(Boolean).join(" ").trim() || null;
|
||||
}
|
||||
|
||||
/** Actual decode chain — does NOT touch the cache or lock. */
|
||||
private async doResolveVin(
|
||||
vin: string,
|
||||
|
||||
Reference in New Issue
Block a user