fix(pl24): Volvo subgroup drilling via vin-group.action HTML
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled

Volvo's VIN catalog is a 3-level vin-group.action?group1=…[&group2=…] HTML
tree; PL24's json-vin-*-group.action JSON endpoints now 404. Add HTML
subgroup extraction (keep links one group-level deeper than the current
path) in ford-legacy fetchSubGroupsByPath, and route vin-group.action?group1=
nodes through getChildren (drill-first, fall back to parts) in
getCategoryWithParts. Recovers Volvo group1→group2 navigation. Leaf parts
handled separately.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-02 02:08:12 +03:00
parent 316a5e014c
commit d792bf0efb
2 changed files with 80 additions and 3 deletions

View File

@@ -130,6 +130,43 @@ export class PL24FordLegacyService {
const html = await this.fetchP4Page(linkPath, serviceName, false, account);
if (!html) return [];
// Volvo (VIN-indexed legacy catalog): vin-group.action?group1=…[&group2=…]
// HTML pages list the next group level as <a href="vin-group.action?…&
// groupN=…"> links. PL24's old JSON json-vin-*-group.action endpoints now
// 404, so we scrape the HTML here. Keep only links with MORE groupN=
// params than the current path (one level deeper); drop sibling/parent nav
// and the VIN-dialog crumb. An empty result ⇒ this node is a leaf (parts).
if (linkPath.includes("vin-group.action")) {
const cfg = getServiceConfig(serviceName);
const basePath = cfg ? `${cfg.basePath}/${serviceName}` : `/volvo/${serviceName}`;
const parentDepth = [...linkPath.matchAll(/\bgroup\d+=/g)].length;
const seenHref = new Set<string>();
const subs: PL24MainGroup[] = [];
const anchorRe =
/<a[^>]+href="([^"]*vin-group\.action\?[^"]*group\d+=[^"]*)"[^>]*>([\s\S]*?)<\/a>/gi;
for (const m of html.matchAll(anchorRe)) {
const href = m[1].replace(/&amp;/g, "&");
if (href.includes("openVinDialog")) continue;
const childDepth = [...href.matchAll(/\bgroup\d+=/g)].length;
if (childDepth <= parentDepth) continue; // sibling/parent nav, not a child
if (seenHref.has(href)) continue;
seenHref.add(href);
const name = m[2]
.replace(/<[^>]+>/g, " ")
.replace(/&nbsp;/g, " ")
.replace(/\s+/g, " ")
.trim()
.replace(/^\d+\s+/, "");
if (name.length < 2) continue;
const lp = href.startsWith("/") ? href : `${basePath}/${href}`;
subs.push({ id: href, code: href, name, linkPath: lp });
}
if (subs.length > 0) {
await this.redis.setJson(cacheKey, subs, 86400);
}
return subs;
}
// Opel/Ford: json-main-group.action → { vCfgData: [...] } (Opel) OR { maingroups: [...] } (Ford)
if (linkPath.includes("json-main-group.action")) {
const basePath = linkPath.substring(0, linkPath.lastIndexOf("/") + 1);