Files
bigwo/test2/server/services/pinyinProductMatcher.js

178 lines
8.2 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 拼音产品名模糊匹配器
* 系统化方案自动覆盖所有中文产品名的同音字ASR误识别
*
* 原理:
* 1. 启动时为每个产品名生成"拼音签名"(声母+韵母序列)
* 2. 运行时将ASR文本的滑动窗口转为拼音签名
* 3. 签名匹配则用正确产品名替换
*
* 优势:新增产品只需加入 PRODUCTS 列表即可自动覆盖无需手写regex
*/
// ============ 字符→拼音映射表 ============
// 仅覆盖产品名用字 + 常见ASR同音替代字~350字
const P = {
// --- a ---
'阿': 'a', '啊': 'a',
'爱': 'ai', '艾': 'ai', '哎': 'ai',
'安': 'an', '暗': 'an', '按': 'an', '氨': 'an', '胺': 'an',
'昂': 'ang',
// --- b ---
'八': 'ba', '巴': 'ba',
'白': 'bai', '百': 'bai', '柏': 'bai',
'包': 'bao', '宝': 'bao', '保': 'bao', '报': 'bao', '煲': 'bao', '苞': 'bao', '胞': 'bao',
'北': 'bei', '被': 'bei', '背': 'bei', '贝': 'bei', '备': 'bei', '辈': 'bei', '杯': 'bei', '倍': 'bei',
'本': 'ben', '苯': 'ben', '奔': 'ben', '笨': 'ben',
// --- c ---
'采': 'cai', '彩': 'cai', '菜': 'cai', '蔡': 'cai', '猜': 'cai', '财': 'cai', '材': 'cai', '才': 'cai',
'草': 'cao', '操': 'cao', '曹': 'cao', '槽': 'cao',
'茶': 'cha', '查': 'cha', '差': 'cha', '插': 'cha', '察': 'cha',
'纯': 'chun', '唇': 'chun', '春': 'chun', '醇': 'chun', '蠢': 'chun',
'萃': 'cui', '翠': 'cui', '脆': 'cui', '粹': 'cui', '催': 'cui',
// --- d ---
'大': 'da',
'蛋': 'dan', '旦': 'dan', '但': 'dan', '淡': 'dan', '弹': 'dan', '担': 'dan',
'毒': 'du', '独': 'du', '度': 'du', '读': 'du', '督': 'du',
// --- e ---
// --- f ---
'发': 'fa', '法': 'fa', '罚': 'fa',
'反': 'fan', '返': 'fan', '犯': 'fan', '翻': 'fan', '范': 'fan', '饭': 'fan',
'非': 'fei', '飞': 'fei', '费': 'fei', '肺': 'fei', '废': 'fei', '吠': 'fei',
'肤': 'fu', '夫': 'fu', '服': 'fu', '福': 'fu', '付': 'fu', '副': 'fu', '附': 'fu', '府': 'fu', '腐': 'fu', '辅': 'fu', '浮': 'fu', '扶': 'fu', '复': 'fu',
// --- g ---
'格': 'ge', '隔': 'ge', '革': 'ge', '各': 'ge', '阁': 'ge', '葛': 'ge', '骼': 'ge',
'骨': 'gu', '谷': 'gu', '古': 'gu', '鼓': 'gu', '估': 'gu', '故': 'gu', '顾': 'gu',
'关': 'guan', '管': 'guan', '官': 'guan', '馆': 'guan',
// --- h ---
'好': 'hao', '号': 'hao', '浩': 'hao', '耗': 'hao', '豪': 'hao',
'衡': 'heng', '横': 'heng', '恒': 'heng', '亨': 'heng',
'红': 'hong', '洪': 'hong', '宏': 'hong', '鸿': 'hong',
'黑': 'hei', '嘿': 'hei',
'活': 'huo', '火': 'huo', '获': 'huo', '霍': 'huo', '货': 'huo', '祸': 'huo',
'黄': 'huang', '皇': 'huang', '荒': 'huang', '慌': 'huang', '煌': 'huang', '惶': 'huang',
// --- j ---
'基': 'ji', '机': 'ji', '鸡': 'ji', '积': 'ji', '极': 'ji', '几': 'ji', '计': 'ji', '记': 'ji', '级': 'ji',
'见': 'jian', '健': 'jian', '剑': 'jian', '键': 'jian', '建': 'jian', '件': 'jian', '检': 'jian', '简': 'jian', '减': 'jian', '渐': 'jian', '坚': 'jian', '尖': 'jian', '肩': 'jian',
'交': 'jiao', '教': 'jiao', '角': 'jiao', '焦': 'jiao', '较': 'jiao', '觉': 'jiao', '胶': 'jiao', '叫': 'jiao', '酵': 'jiao',
'节': 'jie', '结': 'jie', '洁': 'jie', '杰': 'jie', '接': 'jie', '揭': 'jie', '截': 'jie',
'菌': 'jun', '军': 'jun', '均': 'jun', '君': 'jun', '俊': 'jun',
// --- k ---
'抗': 'kang', '康': 'kang', '慷': 'kang',
'口': 'kou',
// --- l ---
'乐': 'le', '勒': 'le',
'力': 'li', '利': 'li', '立': 'li', '厉': 'li', '励': 'li', '历': 'li', '丽': 'li', '离': 'li', '莉': 'li', '礼': 'li', '理': 'li', '李': 'li', '里': 'li',
'藜': 'li', '梨': 'li', '黎': 'li',
'绿': 'lv',
'芦': 'lu', '炉': 'lu', '路': 'lu', '鹿': 'lu', '鲁': 'lu', '卢': 'lu', '露': 'lu', '陆': 'lu', '庐': 'lu',
'酪': 'lao', '烙': 'lao',
'落': 'luo', '络': 'luo',
// --- m ---
'面': 'mian', '免': 'mian', '棉': 'mian', '眠': 'mian', '绵': 'mian', '勉': 'mian',
'免': 'mian',
// --- n ---
// --- p ---
'排': 'pai', '牌': 'pai', '拍': 'pai', '派': 'pai',
'葡': 'pu', '铺': 'pu', '浦': 'pu', '蒲': 'pu',
// --- q ---
'腔': 'qiang',
// --- r ---
'乳': 'ru', '如': 'ru', '入': 'ru', '儒': 'ru',
// --- s ---
'霜': 'shuang', '双': 'shuang', '爽': 'shuang',
'水': 'shui', '睡': 'shui', '谁': 'shui',
'舒': 'shu', '书': 'shu', '叔': 'shu', '输': 'shu', '树': 'shu', '竖': 'shu',
'生': 'sheng', '声': 'sheng', '胜': 'sheng', '升': 'sheng', '省': 'sheng', '圣': 'sheng',
'素': 'su', '速': 'su', '诉': 'su', '苏': 'su', '塑': 'su',
'酸': 'suan', '算': 'suan', '蒜': 'suan',
// --- t ---
'萄': 'tao', '逃': 'tao', '淘': 'tao', '桃': 'tao', '陶': 'tao', '套': 'tao', '讨': 'tao',
'陀': 'tuo', '驼': 'tuo', '拖': 'tuo', '脱': 'tuo', '托': 'tuo',
'酮': 'tong', '铜': 'tong', '同': 'tong', '桐': 'tong', '童': 'tong', '痛': 'tong', '通': 'tong', '统': 'tong',
// --- w ---
// --- x ---
'细': 'xi', '希': 'xi', '西': 'xi', '系': 'xi', '息': 'xi', '稀': 'xi', '席': 'xi', '吸': 'xi',
'纤': 'xian', '先': 'xian', '鲜': 'xian', '仙': 'xian', '险': 'xian', '显': 'xian', '线': 'xian', '限': 'xian', '县': 'xian', '现': 'xian', '献': 'xian',
'小': 'xiao',
// --- y ---
'眼': 'yan', '演': 'yan', '验': 'yan', '烟': 'yan', '严': 'yan', '颜': 'yan', '盐': 'yan', '言': 'yan', '岩': 'yan', '延': 'yan',
'氧': 'yang', '养': 'yang', '仰': 'yang', '样': 'yang', '洋': 'yang', '央': 'yang', '阳': 'yang',
'益': 'yi', '意': 'yi', '易': 'yi', '亿': 'yi', '以': 'yi', '艺': 'yi', '忆': 'yi', '异': 'yi', '议': 'yi', '翼': 'yi', '衣': 'yi', '依': 'yi', '一': 'yi',
'饮': 'yin', '引': 'yin', '印': 'yin', '隐': 'yin', '银': 'yin', '音': 'yin',
'应': 'ying', '映': 'ying', '影': 'ying', '英': 'ying', '营': 'ying', '迎': 'ying',
'育': 'yu', '玉': 'yu', '域': 'yu', '遇': 'yu', '雨': 'yu', '宇': 'yu', '御': 'yu', '语': 'yu', '鱼': 'yu',
'原': 'yuan', '圆': 'yuan', '远': 'yuan', '园': 'yuan', '元': 'yuan', '源': 'yuan', '缘': 'yuan',
// --- z ---
'籽': 'zi', '子': 'zi', '紫': 'zi', '自': 'zi', '字': 'zi',
'转': 'zhuan', '赚': 'zhuan', '砖': 'zhuan', '专': 'zhuan',
};
// 需要拼音匹配的中文产品名3字及以上避免2字误匹配
const PRODUCTS = [
// 5字
'细胞抗氧素',
// 4字
'胶原蛋白', '白藜芦醇', '好转反应', '阿育吠陀',
// 3字
'活力健', '倍力健', '氨基酸', '益生菌', '辅酵素',
'葡萄籽', '排毒饮', '乳酪煲', '草本茶', '异黄酮',
'骨骼健', '舒采健', '衡醇饮', '洁面乳', '爽肤水',
];
// ============ 构建拼音索引 ============
function toPinyinKey(text) {
const parts = [];
for (const ch of text) {
const py = P[ch];
if (!py) return null; // 未知字符,跳过此窗口
parts.push(py);
}
return parts.join('-');
}
const pinyinIndex = new Map(); // pinyin-key → correct product name
for (const name of PRODUCTS) {
const key = toPinyinKey(name);
if (key) {
pinyinIndex.set(key, name);
}
}
// 按长度分组,方便按长度优先匹配
const productLengths = [...new Set(PRODUCTS.map(p => p.length))].sort((a, b) => b - a);
// ============ 运行时匹配 ============
function pinyinMatchProducts(text) {
if (!text || typeof text !== 'string') return text || '';
let result = text;
// 从长窗口到短窗口扫描,避免短匹配覆盖长匹配
for (const len of productLengths) {
if (result.length < len) continue;
for (let i = 0; i <= result.length - len; i++) {
const window = result.substring(i, i + len);
// 快速跳过:窗口含非中文字符
if (!/^[\u4e00-\u9fff]+$/.test(window)) continue;
const key = toPinyinKey(window);
if (!key) continue;
const product = pinyinIndex.get(key);
if (product && window !== product) {
console.log(`[PinyinMatcher] "${window}" → "${product}" (pinyin: ${key})`);
result = result.substring(0, i) + product + result.substring(i + len);
// 跳过已替换部分
i += product.length - 1;
}
}
}
return result;
}
module.exports = {
pinyinMatchProducts,
toPinyinKey,
pinyinIndex,
P,
PRODUCTS,
};