From 8d284f64429edc32a502149cecbe6372ac20f296 Mon Sep 17 00:00:00 2001 From: semih Date: Thu, 17 Sep 2026 14:47:53 +0300 Subject: [PATCH] =?UTF-8?q?fix(pl24):=20Opel/Hyundai/Kia=20decode'unda=200?= =?UTF-8?q?=20kategori=20=E2=80=94=20tablo=20sat=C4=B1rlar=C4=B1=20okunmuy?= =?UTF-8?q?ordu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Faz 1 / adım 6 (analiz: /home/s/ss/plv2.md, bulgu p4legacy-03). SORUN: Bu markalar ana gruplarını ile DEĞİL, satır attribute'uyla veriyor: Opel Hyundai BOBODY decodeVin yolu yalnız anchor tarayan parseP4NavigationCategories'i kullanıyordu → sayfa listeyi taşıdığı hâlde 0 kategori. Prod ölçümü (raw_data.categories): Opel 177/181 (%97,8), Hyundai 12/12, Kia 6/6, Nissan 1/1 araç decode anında BOŞ. (Kategori tablosu sonradan lazy-seed ile doluyor, ama decode kategorisiz döndüğü için kullanıcı ilk açılışta boş görüyor ve prefetch self-seed'e zorlanıyor.) Üç ayrı kusur vardı: 1. `parseFordGroupsFromHtml` bu satırları zaten anlıyor ama decode'dan hiç çağrılmıyordu → kategori zincirine fallback olarak eklendi (JSON ucu → tc-data-row tablosu → anchor taraması). 2. Volvo için konmuş `vin-group.action && !group1=` guard'ı Hyundai/Kia'nın AYNI ucu `mainGroup=` anahtarıyla kullanan gerçek ana gruplarını da eliyordu → artık `group1=` veya `mainGroup=` taşıyan satırlar korunuyor. 3. Satır attribute'ları HTML-escaped (`&`), bu yüzden `[?&]mainGroup=` kontrolü hiç tutmuyordu → URL artık ayrıştırma anında entity-decode ediliyor (saklanan linkPath de böylece temiz). Test: yeni `pl24-p4-groups.spec.ts` + 2026-09-16 canlı yakalamalarından kırpılmış fixture'lar (`__fixtures__/p4_{opel,hyundai,ford,nissan}.html`). Opel 31 ana grup ("BODY SHELL AND PANELS"…), Hyundai 6 ("BODY"…), anchor-only parser'ın bu sayfalarda hâlâ 0 döndüğü (fallback'in gerekçesi), Ford sharedCatCode scope satırlarının bozulmadığı ve Nissan model-seçim satırlarının elendiği kilitlendi. 213 test geçti; tsc + biome temiz. NOT: db_hit kısa devresi yüzünden mevcut araçlar kendiliğinden düzelmez; kazanç yeni decode'larda. Toplu yeniden decode Faz 2'de. Co-Authored-By: Claude Opus 5 (1M context) --- .../pl24/__fixtures__/p4_ford.html | 17 +++ .../pl24/__fixtures__/p4_hyundai.html | 13 +++ .../pl24/__fixtures__/p4_nissan.html | 109 ++++++++++++++++++ .../pl24/__fixtures__/p4_opel.html | 38 ++++++ .../pl24/pl24-ford-legacy.service.ts | 25 +++- .../integrations/pl24/pl24-p4-groups.spec.ts | 70 +++++++++++ 6 files changed, 270 insertions(+), 2 deletions(-) create mode 100644 apps/api/src/integrations/pl24/__fixtures__/p4_ford.html create mode 100644 apps/api/src/integrations/pl24/__fixtures__/p4_hyundai.html create mode 100644 apps/api/src/integrations/pl24/__fixtures__/p4_nissan.html create mode 100644 apps/api/src/integrations/pl24/__fixtures__/p4_opel.html create mode 100644 apps/api/src/integrations/pl24/pl24-p4-groups.spec.ts diff --git a/apps/api/src/integrations/pl24/__fixtures__/p4_ford.html b/apps/api/src/integrations/pl24/__fixtures__/p4_ford.html new file mode 100644 index 0000000..9d9553b --- /dev/null +++ b/apps/api/src/integrations/pl24/__fixtures__/p4_ford.html @@ -0,0 +1,17 @@ + +Ford WF0RXXGCDRAP80417: Kuga - CBV (2008, 2012) - partslink24 + + + + + + + + + + + + +
Kuga - CBV (2008, 2012)
ZE - Accessories (ZE)
ZF - Fluids & Maintenance Products (ZF)
C20DD0X - 2.0 DI Diesel
Engine Type = All 2.0L Duratorq Engines 
CMPS6 - 6 Speed Trans Powershift
Transmission = 6 Speed Powershift 6DCT450 - MPS6 
CVPTO - Transfer Case
Transmission = 6 Speed Powershift 6DCT450 - MPS6 
1Information And Customisation
2Chassis
4Electrical
5Body And Paint
\ No newline at end of file diff --git a/apps/api/src/integrations/pl24/__fixtures__/p4_hyundai.html b/apps/api/src/integrations/pl24/__fixtures__/p4_hyundai.html new file mode 100644 index 0000000..968cbd4 --- /dev/null +++ b/apps/api/src/integrations/pl24/__fixtures__/p4_hyundai.html @@ -0,0 +1,13 @@ + +Hyundai KMHCT41BAGU283791: ACCENT 15 - partslink24 + + + + + + + + +
BOBODY
CHCHASSIS
ELELECTRIC
ENENGINE
MITRANSMISSION
TRTRIM
\ No newline at end of file diff --git a/apps/api/src/integrations/pl24/__fixtures__/p4_nissan.html b/apps/api/src/integrations/pl24/__fixtures__/p4_nissan.html new file mode 100644 index 0000000..017c416 --- /dev/null +++ b/apps/api/src/integrations/pl24/__fixtures__/p4_nissan.html @@ -0,0 +1,109 @@ + +Nissan - partslink24 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Repair & Maintenance Information
S14200SX
Z33350Z
Z34370Z
B10RSALMERA
G15RAALMERA
N15ALMERA
N16ALMERA JPN MAKE
V10MALMERA TINO
N16EALMERA UK MAKE
L33ALTIMA
L34ALTIMA
FE0ARIYA
TK3ATLEON
F24MCABSTAR
Z12CUBE
HBD0RDATSUN MI-DO
HBD0DATSUN MI-DO
BD0RDATSUN ON-DO
BD0DATSUN ON-DO
ME0ME-NV200 SPAIN
R35GT-R
D22SHARDBODY
X70INTERSTAR
XDDINTERSTAR
XDEINTERSTAR
F16EJUKE
F15JUKE JPN MAKE
F15EJUKE UK MAKE
D22KING CAB
LCD22KING CAB
X76KUBISTAR
ZE0LEAF
ZE0ELEAF UK MAKE
ZE1ELEAF UK MAKE
ZE2ELEAF UK MAKE
A32MAXIMA
A36MAXIMA
CA33MAXIMA
K11EMICRA
K12EMICRA
K15MICRA
CK12EMICRA C+C
K14FRMICRA FLIN MAKE
K13KKMICRA IND MAKE
K13KMICRA IND MAKE
Z50MURANO
Z51MURANO
Z51RMURANO RUS MAKE
Z52RMURANO RUS MAKE
D40MNAVARA
D23MNAVARA NP300
E11ENOTE UK MAKE
E12ENOTE UK MAKE
D22SSNP300 SAF MAKE
M20NV200
M20MNV200 SPAINMAKE
X61NV250
X82NV300/PRIMASTAR
X62NV400
X62BNV400/INTERSTAR
R51MPATHFINDER
R52RPATHFINDER
R53PATHFINDER
R52RRPATHFINDER RUS
Y62PATROL
Y61PATROL(GR)
UA0PIXO
X83PRIMASTAR
P11EPRIMERA
P12EPRIMERA
W10PRIMERA WAGON
WP11EPRIMERA WAGON
C13MPULSAR
J10EQASHQAI
J12EQASHQAI
J11RQASHQAI RUSSIA
J11EQASHQAI UK MAKE
JJ10EQASHQAI+2
B17RRSENTRA RUS MAKE
B17RSENTRA RUS MAKE
C23MSERENA
Y10SUNNY WAGON
J31TEANA
J32TEANA
J32RTEANA RUS MAKE
L33RTEANA RUS MAKE
D10TERRANO
R50TERRANO
R20TERRANO2
C11XTIIDA
C13RTIIDA RUS MAKE
SC11XTIIDA SEDAN
XFKTOWNSTAR/e-TOWNSTAR
AGT32X-TRAIL
T30X-TRAIL
T32X-TRAIL
T33X-TRAIL
T31X-TRAIL JPNMAKE
T31RX-TRAIL RUSMAKE
T32RRX-TRAIL RUSMAKE
T32RX-TRAIL RUSMAKE
\ No newline at end of file diff --git a/apps/api/src/integrations/pl24/__fixtures__/p4_opel.html b/apps/api/src/integrations/pl24/__fixtures__/p4_opel.html new file mode 100644 index 0000000..a94e525 --- /dev/null +++ b/apps/api/src/integrations/pl24/__fixtures__/p4_opel.html @@ -0,0 +1,38 @@ + +Opel W0LPD5EC9EG058575: P10 - ASTRA-J [2010-2020] - partslink24 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ABODY SHELL AND PANELS
BBODY EXTERIOR FITTINGS
CBODY INTERIOR FITTINGS
DBODY INTERIOR TRIM
EENGINE AND CLUTCH
FCOOLING
GFUEL AND EXHAUST
HTRANSMISSION
JBRAKES
KFRONT AXLE AND SUSPENSION
LSTEERING
MREAR AXLE AND SUSPENSION
NROAD WHEELS
PELECTRICAL
QACCESSORIES
RSPECIAL OPTIONS / DUAL FUEL
ZPAINTS
1PAINTS
2WIRING HARNESS REPAIR KITS
3CAR CARE MATERIALS
4ADHESIVES / REPAIR KITS
5ANTI-FREEZE AND COOLANT
6BODY REPAIR MATERIALS
7BRAKE AND CLUTCH FLUID
8OIL
9GREASES AND PASTES
ASEALANT
BTOOLS AND PERSONAL PROTECTION
CMISCELLANEOUS
DEMISSION CONTROL FLUID / FUEL ADDITIVES
SDIAGNOSTIC DEVICES
\ No newline at end of file diff --git a/apps/api/src/integrations/pl24/pl24-ford-legacy.service.ts b/apps/api/src/integrations/pl24/pl24-ford-legacy.service.ts index e6c53e4..486c347 100644 --- a/apps/api/src/integrations/pl24/pl24-ford-legacy.service.ts +++ b/apps/api/src/integrations/pl24/pl24-ford-legacy.service.ts @@ -3655,7 +3655,10 @@ export class PL24FordLegacyService { segment.match(/\bjsonurl="([^"]+)"/); if (!urlMatch) continue; - const url = urlMatch[1]; + // Row attributes are HTML-escaped (`&`), so every query-param check + // below — and the stored linkPath — must work on the decoded URL. Without + // this `[?&]mainGroup=` never matched Hyundai/Kia rows. + const url = urlMatch[1].replace(/&/g, "&").replace(/&/g, "&"); // Skip vehicle.action rows — those are sub-model selectors, not part group links if (url.includes("vehicle.action")) continue; // Skip header/breadcrumb navigation links that leak into the group table as @@ -3664,7 +3667,16 @@ export class PL24FordLegacyService { // categories use group.action / group-detail.action / json-(main|sub)-group.action, // none of which match these. Volvo's vin-group.action?...group1=... is kept. if (/(portal|logout)\.action/i.test(url)) continue; - if (url.includes("vin-group.action") && !url.includes("group1=")) continue; + // Volvo's vin-group.action rows are model pickers unless they carry + // group1=; Hyundai/Kia use the SAME endpoint for real main groups but key + // them with mainGroup= (BO/CH/EL/EN/MI/TR). Keep both, drop the rest. + if ( + url.includes("vin-group.action") && + !url.includes("group1=") && + !/[?&]mainGroup=/i.test(url) + ) { + continue; + } if (/^https?:\/\//i.test(url) && !/\.action(\?|$)/i.test(url)) continue; if (seen.has(url)) continue; seen.add(url); @@ -4058,6 +4070,15 @@ export class PL24FordLegacyService { return []; }); } + // Opel/Vauxhall and Hyundai/Kia ship their main groups as `tc-data-row` + // table rows whose link lives in a `url=`/`jsonurl=` ATTRIBUTE, not in an + //
. parseP4NavigationCategories only reads anchors, so these brands + // decoded with ZERO categories (prod: Opel 169/173, Hyundai 11/11, Kia 5/5) + // even though the page carries the full list. parseFordGroupsFromHtml + // already understands those rows — it simply was never called from decode. + if (categories.length === 0) { + categories = this.parseFordGroupsFromHtml(html, serviceName, ""); + } if (categories.length === 0) { categories = this.parseP4NavigationCategories(html); } diff --git a/apps/api/src/integrations/pl24/pl24-p4-groups.spec.ts b/apps/api/src/integrations/pl24/pl24-p4-groups.spec.ts new file mode 100644 index 0000000..a19a823 --- /dev/null +++ b/apps/api/src/integrations/pl24/pl24-p4-groups.spec.ts @@ -0,0 +1,70 @@ +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import { describe, expect, it } from "vitest"; +import { PL24FordLegacyService } from "./pl24-ford-legacy.service"; + +/** + * Opel / Hyundai / Kia "0 kategori" regresyon kilidi (plv2.md, bulgu p4legacy-03). + * + * Bu markalar ana gruplarını DEĞİL, `` satır attribute'unda taşıyor. Decode yolu yalnız anchor + * tarayan parser'ı kullandığı için prod'da Opel 169/173, Hyundai 11/11, Kia 5/5 + * araç SIFIR kategoriyle kaydedilmişti — sayfa listeyi taşıdığı hâlde. + * + * Fixture'lar 2026-09-16 canlı keşfinden (plv2-artefakt/), yalnız ana grup + * tablosuna kırpılmış hâlleri. + */ + +const fixture = (name: string) => + readFileSync(join(__dirname, "__fixtures__", `${name}.html`), "utf-8"); + +// parseFordGroupsFromHtml saf bir metin işleyicisi: bağımlılıklar kullanılmıyor. +const svc = new PL24FordLegacyService( + {} as never, // authService + { get: (_k: string, d?: unknown) => d } as never, // configService + {} as never, // redis + {} as never, // storage +) as unknown as { + parseFordGroupsFromHtml( + html: string, + serviceName: string, + familyId: string, + ): Array<{ code: string; nameEn: string; linkPath?: string }>; + parseP4NavigationCategories(html: string): Array<{ nameEn: string }>; +}; + +describe("P4 ana grup tablosu — Opel/Hyundai (0 kategori regresyonu)", () => { + it("Opel: jsonurl satırlarından 31 ana grubu çıkarır", () => { + const groups = svc.parseFordGroupsFromHtml(fixture("p4_opel"), "opel_parts", ""); + expect(groups.length).toBe(31); + expect(groups[0].nameEn).toBe("BODY SHELL AND PANELS"); + expect(groups[0].code).toContain("json-vin-main-group.action"); + expect(groups[0].code).toContain("mainGroupId=394"); + // Adların hepsi gerçek metin olmalı (kod artığı değil) + expect(groups.every((g) => /[A-Za-z]{3,}/.test(g.nameEn))).toBe(true); + }); + + it("Hyundai: mainGroup= anahtarlı vin-group satırları artık elenmez", () => { + const groups = svc.parseFordGroupsFromHtml(fixture("p4_hyundai"), "hyundai_parts", ""); + expect(groups.length).toBe(6); + const names = groups.map((g) => g.nameEn); + expect(names).toContain("BODY"); + expect(groups[0].code).toContain("mainGroup=BO"); + }); + + it("anchor-only parser bu sayfalarda hâlâ boş döner (fallback'in gerekçesi)", () => { + expect(svc.parseP4NavigationCategories(fixture("p4_opel")).length).toBe(0); + expect(svc.parseP4NavigationCategories(fixture("p4_hyundai")).length).toBe(0); + }); + + it("Ford: scope satırları (sharedCatCode) bozulmadan okunur", () => { + const groups = svc.parseFordGroupsFromHtml(fixture("p4_ford"), "fordp_parts", ""); + expect(groups.length).toBeGreaterThanOrEqual(6); + expect(groups.some((g) => g.code.includes("sharedCatCode=ZE"))).toBe(true); + }); + + it("Nissan: model seçim satırları ana grup sayılmaz (vehicle.action elenir)", () => { + const groups = svc.parseFordGroupsFromHtml(fixture("p4_nissan"), "nissan_parts", ""); + expect(groups.every((g) => !g.code.includes("vehicle.action"))).toBe(true); + }); +});