diff --git a/apps/api/src/product-families/origin-name.parser.spec.ts b/apps/api/src/product-families/origin-name.parser.spec.ts index 52d016b..7e8cc54 100644 --- a/apps/api/src/product-families/origin-name.parser.spec.ts +++ b/apps/api/src/product-families/origin-name.parser.spec.ts @@ -56,6 +56,14 @@ describe('parseOriginName', () => { expect(r.warehouseLabel).toBeNull(); }); + it('半角/全角括号混用(美国(不包邮)…)按混合对解析', () => { + const r = parseOriginName('美国(不包邮)女士高弹中长款瑜伽运动裤-JSD004-单面印花-美西洛杉矶一仓'); + expect(r.country).toBe('美国'); + expect(r.logisticsLabel).toBe('不包邮'); + expect(r.productName).toBe('女士高弹中长款瑜伽运动裤'); + expect(r.skuCode).toBe('JSD004'); + }); + it('一段名/空值/null 安全', () => { expect(parseOriginName('随便一个名字').skuCode).toBeNull(); expect(parseOriginName('随便一个名字').country).toBe('随便一个名字'); diff --git a/apps/api/src/product-families/origin-name.parser.ts b/apps/api/src/product-families/origin-name.parser.ts index 7527ec2..135789d 100644 --- a/apps/api/src/product-families/origin-name.parser.ts +++ b/apps/api/src/product-families/origin-name.parser.ts @@ -26,24 +26,20 @@ const EMPTY: ParsedOriginName = { }; function splitSegment1(seg: string): Pick { - const pairs: Array<[string, string]> = [ - ['(', ')'], - ['(', ')'], - ]; - for (const [open, close] of pairs) { - const openAt = seg.indexOf(open); - if (openAt >= 0) { - const closeAt = seg.indexOf(close, openAt); - if (closeAt > openAt) { - return { - country: seg.slice(0, openAt).trim() || null, - logisticsLabel: seg.slice(openAt + 1, closeAt).trim() || null, - productName: seg.slice(closeAt + 1).trim() || null, - }; - } + // SDS 上游名称存在半角/全角括号混用(如 `美国(不包邮)…`),先归一成全角再配对 + const normalized = seg.replace(/\(/g, '(').replace(/\)/g, ')'); + const openAt = normalized.indexOf('('); + if (openAt >= 0) { + const closeAt = normalized.indexOf(')', openAt); + if (closeAt > openAt) { + return { + country: normalized.slice(0, openAt).trim() || null, + logisticsLabel: normalized.slice(openAt + 1, closeAt).trim() || null, + productName: normalized.slice(closeAt + 1).trim() || null, + }; } } - return { country: seg.trim() || null, logisticsLabel: null, productName: null }; + return { country: normalized.trim() || null, logisticsLabel: null, productName: null }; } export function parseOriginName(name: string | null | undefined): ParsedOriginName {