function normalizeCustomerKey(value) { return String(value || '') .replace(/[(]/g, '(') .replace(/[)]/g, ')') .replace(/[,,。.;;、\s]/g, '') .toLowerCase() .trim(); } function customerDisplayName(value = {}, fallback = '') { return value.erpName || value.name || fallback; } function candidateFor(key, value, keyword) { return { key, id: String(value && value.id || ''), name: customerDisplayName(value, key), value, matchedBy: keyword, }; } function matchesCustomer(candidateKey, customer, keyword) { const wanted = normalizeCustomerKey(keyword); const haystacks = [ candidateKey, customer && customer.erpName, customer && customer.name, customer && customer.searchName, ].map(normalizeCustomerKey).filter(Boolean); return haystacks.some((value) => value === wanted || value.includes(wanted) || wanted.includes(value)); } function resolveCustomerKeyword(map, keyword, kind = 'customer') { const source = map || {}; const rawKeyword = String(keyword || '').trim(); if (!rawKeyword) { return { status: 'not_found', reason: 'customer_keyword_empty', keyword: rawKeyword, candidates: [], customerMessage: `请补充${kind}关键词。`, }; } if (source[rawKeyword]) { return { status: 'matched', reason: '', keyword: rawKeyword, key: rawKeyword, value: source[rawKeyword], candidates: [candidateFor(rawKeyword, source[rawKeyword], rawKeyword)], }; } const matches = Object.entries(source) .filter(([key, value]) => matchesCustomer(key, value, rawKeyword)) .map(([key, value]) => candidateFor(key, value, rawKeyword)); if (matches.length === 1) { return { status: 'matched', reason: '', keyword: rawKeyword, key: matches[0].key, value: matches[0].value, candidates: matches, }; } if (matches.length > 1) { return { status: 'ambiguous', reason: 'customer_keyword_ambiguous', keyword: rawKeyword, candidates: matches, customerMessage: `${kind}关键词“${rawKeyword}”匹配到多个客户,请确认:${matches.map((item) => item.name).join('、')}`, }; } return { status: 'not_found', reason: 'customer_keyword_not_found', keyword: rawKeyword, candidates: [], customerMessage: `${kind}关键词“${rawKeyword}”没有匹配到 ERP 客户,请补充更完整名称。`, }; } function resolveCustomerOrThrow(map, keyword, kind = 'customer', options = {}) { const result = resolveCustomerKeyword(map, keyword, kind); if (result.status === 'matched') return result.value; if (result.status === 'not_found' && options.allowPlainName) return {}; const error = new Error(result.status === 'ambiguous' ? `customer keyword ambiguous for ${kind}: ${keyword}` : `customer keyword not found for ${kind}: ${keyword}`); error.code = result.reason; error.customerMessage = result.customerMessage; error.candidates = result.candidates; throw error; } module.exports = { normalizeCustomerKey, resolveCustomerKeyword, resolveCustomerOrThrow, };