chore: CRM-NEW baseline

This commit is contained in:
wangliang
2026-10-23 10:35:28 +08:00
commit aa499f6dbf
111 changed files with 15126 additions and 0 deletions
+54
View File
@@ -0,0 +1,54 @@
'use strict';
// 购买意向识别 + post-LLM 追问兜底(不依赖 LLM 自觉)。
const PRODUCT_RE = /(戒指|项链|耳环|耳钉|手链|脚链|珠宝|饰品|发饰|包|服装|连衣裙|ring|necklace|earring|bracelet|anklet|jewelry|jewellery|accessor|hair ?clip|bag|dress|clothing|pendant|charm)/i;
const QUANTITY_RE = /(\d+\s*(pcs?|pieces?|units?|sets?|pairs?|kilos?|kg|thousand|k\b|件|条|只|个|套|对)|moq|bulk|wholesale|批发|采购)/i;
const ASK = {
zh: '\n\n顺便问一下,方便告诉我贵公司名称和联系邮箱吗?这样我们好为您建立批发账户并跟进需求。',
fr: '\n\nAu fait, pourriez-vous me donner le nom de votre société et un email de contact ? Cela nous permettra de créer votre compte grossiste et de suivre votre demande.',
de: '\n\nDürfte ich noch den Namen Ihrer Firma und eine Kontakt-E-Mail haben? So können wir Ihr Großhandelskonto anlegen und Ihre Anfrage verfolgen.',
it: '\n\nA proposito, potrebbe darmi il nome della sua azienda e una email di contatto? Così possiamo creare il suo account all\'ingrosso e seguire la richiesta.',
es: '\n\nPor cierto, ¿podría darme el nombre de su empresa y un email de contacto? Así podemos crear su cuenta mayorista y dar seguimiento.',
nl: '\n\nTrouwens, mag ik de naam van uw bedrijf en een contact-e-mail hebben? Zo kunnen we uw groothandelsaccount aanmaken en uw aanvraag opvolgen.',
en: '\n\nBy the way, could you share your company name and a contact email? That way we can set up your wholesale account and follow up on your request.',
};
function hasBuyingIntent(text) {
const value = String(text || '');
return PRODUCT_RE.test(value) && QUANTITY_RE.test(value);
}
function missingLeadContacts(slots = {}, knownEmail = '') {
const missing = [];
if (!slots.company) missing.push('company');
if (!slots.email && !slots.phone && !slots.contactName && !knownEmail) missing.push('contact');
return missing;
}
function detectLanguage(text) {
const q = String(text || '');
if (/[\u3400-\u9fff]/.test(q)) return 'zh';
if (/\b(und|nicht|für|mit|bitte|haben|preis|menge|lieferung)\b/i.test(q)) return 'de';
if (/\b(et|pour|avec|bonjour|prix|quantité|livraison|merci)\b/i.test(q)) return 'fr';
if (/\b(e|per|con|prezzo|quantità|spedizione|grazie)\b/i.test(q)) return 'it';
if (/\b(y|para|con|precio|cantidad|envío|gracias)\b/i.test(q)) return 'es';
if (/\b(en|voor|met|prijs|aantal|levering|bedankt)\b/i.test(q)) return 'nl';
return 'en';
}
function buildLeadFollowup(lang) {
return ASK[lang] || ASK.en;
}
function alreadyAsksForLead(reply) {
return /(company|société|entreprise|firma|unternehmen|azienda|empresa|bedrijf|email|e-mail|mail|kontakt|contact|公司|邮箱|邮件)/i.test(String(reply || ''));
}
function appendLeadFollowup(reply, lang) {
const text = String(reply || '').replace(/\s+$/, '');
if (!text || alreadyAsksForLead(text)) return text;
return text + buildLeadFollowup(lang);
}
module.exports = { hasBuyingIntent, missingLeadContacts, detectLanguage, buildLeadFollowup, alreadyAsksForLead, appendLeadFollowup };