76 lines
3.2 KiB
TypeScript
76 lines
3.2 KiB
TypeScript
/**
|
||
* SKU 图文件名 → SKU 编号 / 产品条码 自动判别
|
||
*
|
||
* 依据生产数据的稳定结构(配合 lib/spu-parser.ts 的品类字典):
|
||
* - SKU 编号: [前缀-?]品类码 + 纯数字,如 DNZ038521、AD033601、GBSFY-AD0360、MKHDNZ0409260600987
|
||
* - 组合 SKU: ZH- 开头,或以 + 连接多个有效产品编码;整串都属于 SKU 编号
|
||
* - 产品条码: SKU 编号 + N数字(V数字)? 段,如 DNZ038521N1280V01、CY001306N35V01
|
||
* - 其余(纯序号、02-1、复合串、含中文等)判别不出,交给人工填写
|
||
*
|
||
* 前缀连字符可选:MKH(模块化)等业务前缀直接无连字符拼接品类码;
|
||
* MKH 不在品类字典中,词典层面无歧义。判定的其余部分整串锚定,绝不猜。
|
||
*/
|
||
import { getCategoryCodes, getShopPrefixes } from '@/lib/spu-parser'
|
||
|
||
/** 直接拼接在品类码前的业务前缀(无连字符形态),与店铺前缀一并剥离判定。 */
|
||
const EXTRA_NAKED_PREFIXES = ['MKH']
|
||
|
||
// ZH 是明确的“组合套装”业务前缀。组合段内即使出现 N/V 也不能按条码解释;
|
||
// 每段至少同时包含字母码和数字,避免把 ZH-任意文本误判为 SKU。
|
||
const COMBINATION_SKU_PATTERN = /^ZH-[A-Z]+\d[A-Z0-9]*(?:-[A-Z]+\d[A-Z0-9]*)+$/i
|
||
|
||
export type SkuOrBarcodeDetection =
|
||
| { type: 'barcode'; value: string }
|
||
| { type: 'skuCode'; value: string }
|
||
|
||
function escapeRegExp(value: string): string {
|
||
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||
}
|
||
|
||
function buildPattern(kind: 'barcode' | 'skuCode'): RegExp {
|
||
// 长前缀/长品类码放在 alternation 前面,避免短码抢先匹配(如 AD 抢掉 ADX 前缀的场景)。
|
||
const shops = [...getShopPrefixes(), ...EXTRA_NAKED_PREFIXES]
|
||
.map(escapeRegExp)
|
||
.sort((a, b) => b.length - a.length)
|
||
.join('|')
|
||
const categories = getCategoryCodes()
|
||
.map(escapeRegExp)
|
||
.sort((a, b) => b.length - a.length)
|
||
.join('|')
|
||
|
||
const shopPart = shops ? `(?:(?:${shops})-?)?` : ''
|
||
// N 段必有,V 段可省(生产数据存在 cy001301N35 这种无 V 段的真实条码)。
|
||
const tail = kind === 'barcode' ? 'N\\d+(?:V\\d+)?' : ''
|
||
return new RegExp(`^${shopPart}(?:${categories})\\d+${tail}$`, 'i')
|
||
}
|
||
|
||
const BARCODE_PATTERN = buildPattern('barcode')
|
||
const SKU_CODE_PATTERN = buildPattern('skuCode')
|
||
|
||
/**
|
||
* 判别一个 SKU 图文件名(应先剥掉图片后缀)。
|
||
* 返回 null 表示命名不规范,不自动填,由上传人员人工填写。
|
||
*/
|
||
export function detectSkuOrBarcodeName(name: string): SkuOrBarcodeDetection | null {
|
||
const trimmed = String(name || '').trim()
|
||
if (!trimmed) return null
|
||
|
||
// 组合 SKU 的业务语义优先于普通 N/V 条码规则。
|
||
const parts = trimmed.split('+')
|
||
const isJoinedSku = parts.length > 1 && parts.every(part =>
|
||
SKU_CODE_PATTERN.test(part) || BARCODE_PATTERN.test(part)
|
||
)
|
||
if (COMBINATION_SKU_PATTERN.test(trimmed) || isJoinedSku) {
|
||
return { type: 'skuCode', value: trimmed }
|
||
}
|
||
|
||
// 整串锚定:两条规则天然互斥(skuCode 规则的 \d+$ 不容 N/V 段),顺序无关。
|
||
if (BARCODE_PATTERN.test(trimmed)) {
|
||
return { type: 'barcode', value: trimmed }
|
||
}
|
||
if (SKU_CODE_PATTERN.test(trimmed)) {
|
||
return { type: 'skuCode', value: trimmed }
|
||
}
|
||
return null
|
||
}
|