fix(api): emex source-db lookup uses catalogCode+gid, not SSD

Initial design routed emex lookups through vehicles.rawData.ssd → dump vehicles
→ vehicle_parts. Smoke test against prod ssd values: 0 / 10 matched. EMEX
regenerates the SSD on every decode session, so sase's stored SSD never
matches the SSD the dump scraper recorded for the same physical vehicle.

Pivot to a catalog-wide bridge that actually works:
  catalogs.code    ↔ vehicles.rawData.catalogCode  (e.g. "RENAULT201910")
  part_groups.group_id ↔ categories.externalId    (e.g. "11754")
  → parts via vehicle_parts.group_id (dump's parts.group_id is 100% NULL)

Verified coverage on prod's 8287 unique (catalogCode, gid) pairs: 25/26
catalog codes resolve, 7178 pairs hit a part_group (87%), 5919 of those
return actual parts via vehicle_parts (~71% net). Tradeoff: returns all
parts in the (catalog, group) across every variant in the catalog, so the
result is slightly noisier than the live per-vehicle scrape. Acceptable —
parts overlap heavily and the upstream-call savings outweigh the noise.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-01 15:06:48 +03:00
parent e5ed6b9f36
commit 41acff2ab2
2 changed files with 49 additions and 54 deletions

View File

@@ -1092,16 +1092,19 @@ export class CategoriesService {
} else if (vehicle && category.source === "emex") {
// EMEX: fetch parts + schema image via Puppeteer from QuickDetails URL
try {
// Local dump first (uses vehicles.rawData.ssd persisted by the
// emex mapper). Falls back to live upstream on miss.
const emexSsd = (vehicle.rawData as { ssd?: string } | null)?.ssd;
// Local dump first (catalog-wide via vehicles.rawData.catalogCode +
// category.externalId). The dump's per-vehicle bridge is the EMEX
// SSD, which is session-regenerated and never matches what sase
// stored — so we accept over-return at the catalog level (~71% hit).
// Falls back to live upstream on miss.
const catalogCode = (vehicle.rawData as { catalogCode?: string } | null)?.catalogCode;
let emexResult = await this.emexSourceDb.fetchCategoryParts(
emexSsd,
category.linkPath ?? "",
catalogCode,
category.externalId,
);
if (emexResult) {
this.logger.debug(
`[source-db hit emex] ssd=${emexSsd?.slice(0, 12)}... gid=${category.externalId}`,
`[source-db hit emex] catalog=${catalogCode} gid=${category.externalId}`,
);
} else {
emexResult = await this.emexService.fetchCategoryParts(category.linkPath);