feat(VIP匹配): 改进VIP客户手机号匹配算法并增强调试信息
引入基于分数的匹配机制,为不同匹配策略分配权重 添加匹配分数阈值控制有效匹配 增强调试面板显示更多匹配细节和统计信息
This commit is contained in:
@@ -1082,6 +1082,7 @@ $packages_json = json_encode(array_map(function($package) {
|
||||
// 找出所有可能匹配的VIP客户
|
||||
const potentialMatches = [];
|
||||
let matchedVIP = null;
|
||||
let bestMatchScore = 0;
|
||||
|
||||
for (const vip of window.allVIPCustomers) {
|
||||
if (!vip.phone) continue;
|
||||
@@ -1091,40 +1092,75 @@ $packages_json = json_encode(array_map(function($package) {
|
||||
|
||||
console.log('检查VIP:', vip.customer_name, '手机号:', vipPhone, '标准化后:', normalizedVIPPhone);
|
||||
|
||||
// 多种匹配策略
|
||||
const isMatch =
|
||||
// 精确匹配
|
||||
vipPhone === phone ||
|
||||
normalizedVIPPhone === normalizedInput ||
|
||||
// 包含匹配
|
||||
vipPhone.includes(phone) ||
|
||||
phone.includes(vipPhone) ||
|
||||
normalizedVIPPhone.includes(normalizedInput) ||
|
||||
normalizedInput.includes(normalizedVIPPhone) ||
|
||||
// 后几位匹配(常用于隐私保护)
|
||||
(normalizedInput.length >= 4 && normalizedVIPPhone.endsWith(normalizedInput));
|
||||
// 计算匹配分数
|
||||
let matchScore = 0;
|
||||
|
||||
if (isMatch) {
|
||||
console.log('找到匹配:', vip.customer_name, vipPhone);
|
||||
// 精确匹配(最高优先级)
|
||||
if (vipPhone === phone) {
|
||||
matchScore = 100;
|
||||
} else if (normalizedVIPPhone === normalizedInput) {
|
||||
matchScore = 95;
|
||||
}
|
||||
// 包含匹配(中等优先级)
|
||||
else if (vipPhone.includes(phone)) {
|
||||
matchScore = 90;
|
||||
} else if (phone.includes(vipPhone)) {
|
||||
matchScore = 85;
|
||||
} else if (normalizedVIPPhone.includes(normalizedInput)) {
|
||||
matchScore = 80;
|
||||
} else if (normalizedInput.includes(normalizedVIPPhone)) {
|
||||
matchScore = 75;
|
||||
}
|
||||
// 后几位匹配(低优先级)
|
||||
else if (normalizedInput.length >= 4 && normalizedVIPPhone.endsWith(normalizedInput)) {
|
||||
matchScore = 70;
|
||||
}
|
||||
// 前几位匹配
|
||||
else if (normalizedInput.length >= 4 && normalizedVIPPhone.startsWith(normalizedInput)) {
|
||||
matchScore = 65;
|
||||
}
|
||||
// 部分匹配(最低优先级)
|
||||
else if (normalizedVIPPhone.includes(normalizedInput.substring(normalizedInput.length - 4))) {
|
||||
matchScore = 60;
|
||||
} else if (normalizedInput.length >= 6 && normalizedVIPPhone.includes(normalizedInput.substring(0, 6))) {
|
||||
matchScore = 55;
|
||||
}
|
||||
|
||||
// 如果找到更好的匹配,更新匹配结果
|
||||
if (matchScore > bestMatchScore) {
|
||||
bestMatchScore = matchScore;
|
||||
matchedVIP = vip;
|
||||
break; // 找到第一个匹配就返回
|
||||
console.log('找到更好的匹配:', vip.customer_name, vipPhone, '分数:', matchScore);
|
||||
}
|
||||
|
||||
// 记录潜在匹配供调试
|
||||
if (normalizedVIPPhone.includes(normalizedInput.substring(normalizedInput.length - 3)) ||
|
||||
normalizedInput.includes(normalizedVIPPhone.substring(normalizedVIPPhone.length - 3))) {
|
||||
potentialMatches.push({name: vip.customer_name, phone: vipPhone});
|
||||
if (matchScore >= 50 && matchScore < bestMatchScore) {
|
||||
potentialMatches.push({name: vip.customer_name, phone: vipPhone, score: matchScore});
|
||||
} else if (normalizedVIPPhone.includes(normalizedInput.substring(normalizedInput.length - 3)) ||
|
||||
normalizedInput.includes(normalizedVIPPhone.substring(normalizedVIPPhone.length - 3))) {
|
||||
potentialMatches.push({name: vip.customer_name, phone: vipPhone, score: 40});
|
||||
}
|
||||
}
|
||||
|
||||
console.log('潜在匹配列表:', potentialMatches);
|
||||
console.log('最终匹配结果:', matchedVIP);
|
||||
// 根据分数过滤潜在匹配,只保留分数较高的
|
||||
potentialMatches.sort((a, b) => (b.score || 0) - (a.score || 0));
|
||||
|
||||
if (matchedVIP) {
|
||||
console.log('潜在匹配列表:', potentialMatches);
|
||||
console.log('最终匹配结果:', matchedVIP, '匹配分数:', bestMatchScore);
|
||||
|
||||
// 设置最小匹配分数阈值
|
||||
const MIN_MATCH_SCORE = 55;
|
||||
|
||||
// 只有当匹配分数足够高时才认为是有效匹配
|
||||
const isEffectiveMatch = matchedVIP && bestMatchScore >= MIN_MATCH_SCORE;
|
||||
|
||||
if (isEffectiveMatch) {
|
||||
console.log('确认有效匹配,分数:', bestMatchScore);
|
||||
// 显示VIP客户提示
|
||||
tipDiv.innerHTML = `
|
||||
<strong>👑 检测到VIP客户!</strong><br>
|
||||
该手机号属于 VIP客户:${matchedVIP.customer_name}<br>
|
||||
匹配度: ${bestMatchScore}分<br>
|
||||
<button type="button" onclick="switchToVIPMode(${matchedVIP.id})" class="switch-to-vip-btn">
|
||||
切换到VIP客户模式
|
||||
</button>
|
||||
@@ -1136,12 +1172,17 @@ $packages_json = json_encode(array_map(function($package) {
|
||||
let message = '💡 <strong>提示:</strong>未找到该手机号的VIP客户<br>';
|
||||
message += '如果此客户是VIP客户,请检查手机号是否正确';
|
||||
|
||||
// 如果有潜在匹配,添加提示
|
||||
// 如果有潜在匹配,添加提示并显示匹配分数
|
||||
if (potentialMatches.length > 0) {
|
||||
message += '<br><br><strong>可能相关的VIP客户:</strong><br>';
|
||||
potentialMatches.slice(0, 3).forEach(vip => {
|
||||
message += `- ${vip.name} (${vip.phone})<br>`;
|
||||
const scoreText = vip.score ? ` (匹配度: ${vip.score}分)` : '';
|
||||
message += `- ${vip.name} (${vip.phone})${scoreText}<br>`;
|
||||
});
|
||||
} else if (matchedVIP && bestMatchScore > 0) {
|
||||
// 如果有低分数匹配,也显示出来
|
||||
message += `<br><br><strong>相似手机号的VIP客户:</strong><br>`;
|
||||
message += `- ${matchedVIP.customer_name} (${matchedVIP.phone}) (匹配度: ${bestMatchScore}分)<br>`;
|
||||
}
|
||||
|
||||
tipDiv.innerHTML = message;
|
||||
@@ -1149,13 +1190,17 @@ $packages_json = json_encode(array_map(function($package) {
|
||||
tipDiv.style.display = 'block';
|
||||
}
|
||||
|
||||
// 记录到调试面板
|
||||
logSearchToDebugPanel(phone, matchedVIP, potentialMatches);
|
||||
|
||||
// 增强的调试日志
|
||||
console.log('--- 结束检查手机号 ---');
|
||||
console.log('检查总结: 输入手机号=', phone, '标准化后=', normalizedInput, 'VIP客户总数=', window.allVIPCustomers.length, '有效匹配=', isEffectiveMatch);
|
||||
|
||||
// 记录到调试面板
|
||||
logSearchToDebugPanel(phone, matchedVIP, potentialMatches);
|
||||
// 记录到调试面板,包含更详细的信息
|
||||
logSearchToDebugPanel(phone, matchedVIP, potentialMatches, {
|
||||
normalizedInput,
|
||||
matchScore: bestMatchScore,
|
||||
isEffectiveMatch,
|
||||
vipCount: window.allVIPCustomers.length
|
||||
});
|
||||
}
|
||||
|
||||
// VIP客户搜索调试面板
|
||||
@@ -1232,33 +1277,82 @@ $packages_json = json_encode(array_map(function($package) {
|
||||
}
|
||||
|
||||
// 记录搜索历史到调试面板
|
||||
function logSearchToDebugPanel(phone, result, potentialMatches) {
|
||||
function logSearchToDebugPanel(phone, result, potentialMatches, searchInfo = {}) {
|
||||
const historyElem = document.getElementById('debug-search-history');
|
||||
if (!historyElem) return;
|
||||
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
const { normalizedInput, matchScore, isEffectiveMatch, vipCount } = searchInfo;
|
||||
const logEntry = document.createElement('div');
|
||||
logEntry.style.cssText = 'border-bottom: 1px solid #eee; padding: 5px 0;';
|
||||
logEntry.style.cssText = 'border-bottom: 1px solid #eee; padding: 8px 0; font-size: 12px;';
|
||||
|
||||
let resultHtml = '<span style="color: red;">未匹配</span>';
|
||||
if (result) {
|
||||
resultHtml = `<span style="color: green;">匹配成功: ${result.customer_name} (${result.phone})</span>`;
|
||||
// 构建日志内容
|
||||
let logContent = `
|
||||
<div style="color: #666; font-weight: bold;">[${timestamp}] 搜索: ${phone}</div>
|
||||
`;
|
||||
|
||||
// 添加详细信息
|
||||
if (normalizedInput) {
|
||||
logContent += `
|
||||
<div style="color: #6c757d; margin-left: 10px; font-size: 11px;">标准化: ${normalizedInput}</div>
|
||||
`;
|
||||
}
|
||||
|
||||
logEntry.innerHTML = `
|
||||
<div style="font-weight: bold;">[${timestamp}] 搜索: ${phone}</div>
|
||||
<div>结果: ${resultHtml}</div>
|
||||
`;
|
||||
if (vipCount !== undefined) {
|
||||
logContent += `
|
||||
<div style="color: #6c757d; margin-left: 10px; font-size: 11px;">数据库中VIP客户数: ${vipCount}</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// 显示匹配结果
|
||||
let resultHtml = '<span style="color: red;">未匹配</span>';
|
||||
if (isEffectiveMatch !== undefined) {
|
||||
if (isEffectiveMatch && result) {
|
||||
resultHtml = `<span style="color: green;">匹配成功: ${result.customer_name} (${result.phone})</span>`;
|
||||
if (matchScore !== undefined) {
|
||||
logContent += `
|
||||
<div>结果: ${resultHtml}</div>
|
||||
<div style="color: #28a745; margin-left: 10px; font-size: 11px;">匹配度: ${matchScore}分</div>
|
||||
`;
|
||||
} else {
|
||||
logContent += `
|
||||
<div>结果: ${resultHtml}</div>
|
||||
`;
|
||||
}
|
||||
} else {
|
||||
if (result && matchScore !== undefined) {
|
||||
resultHtml = `<span style="color: orange;">相似匹配: ${result.customer_name} (${result.phone}) (匹配度: ${matchScore}分)</span>`;
|
||||
}
|
||||
logContent += `
|
||||
<div>结果: ${resultHtml}</div>
|
||||
`;
|
||||
}
|
||||
} else if (result) {
|
||||
// 兼容旧版本调用
|
||||
resultHtml = `<span style="color: green;">匹配成功: ${result.customer_name} (${result.phone})</span>`;
|
||||
logContent += `
|
||||
<div>结果: ${resultHtml}</div>
|
||||
`;
|
||||
} else {
|
||||
logContent += `
|
||||
<div>结果: ${resultHtml}</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// 如果有潜在匹配,也显示出来
|
||||
if (potentialMatches && potentialMatches.length > 0) {
|
||||
let potentialHtml = '<div style="margin-top: 2px; font-size: 11px; color: #666;">潜在匹配: ';
|
||||
potentialHtml += potentialMatches.slice(0, 2).map(v => `${v.name} (${v.phone})`).join(', ');
|
||||
potentialHtml += potentialMatches.slice(0, 2).map(v => {
|
||||
const scoreText = v.score ? ` (${v.score}分)` : '';
|
||||
return `${v.name} (${v.phone})${scoreText}`;
|
||||
}).join(', ');
|
||||
if (potentialMatches.length > 2) potentialHtml += '...';
|
||||
potentialHtml += '</div>';
|
||||
logEntry.innerHTML += potentialHtml;
|
||||
logContent += potentialHtml;
|
||||
}
|
||||
|
||||
logEntry.innerHTML = logContent;
|
||||
|
||||
// 添加到历史的顶部
|
||||
if (historyElem.firstChild) {
|
||||
historyElem.insertBefore(logEntry, historyElem.firstChild);
|
||||
@@ -1267,7 +1361,7 @@ $packages_json = json_encode(array_map(function($package) {
|
||||
}
|
||||
|
||||
// 限制历史记录数量
|
||||
while (historyElem.children.length > 10) {
|
||||
while (historyElem.children.length > 20) {
|
||||
historyElem.removeChild(historyElem.lastChild);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user