feat: 添加VIP客户数据验证脚本和搜索调试工具

添加verify_vip_data.php脚本用于验证VIP客户数据完整性
创建vip_search_debug.html调试工具用于测试搜索功能
This commit is contained in:
2025-11-19 02:08:20 +08:00
parent 9354a5d3f4
commit 57bac51bf4
2 changed files with 735 additions and 0 deletions
+245
View File
@@ -0,0 +1,245 @@
<?php
// VIP客户数据验证脚本
header('Content-Type: text/html; charset=utf-8');
// 数据库连接
function connectDatabase() {
$servername = "localhost";
$username = "root";
$password = "";
$database = "carwash_booking";
try {
$pdo = new PDO("mysql:host=$servername;dbname=$database;charset=utf8", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch(PDOException $e) {
die("数据库连接失败: " . $e->getMessage());
}
}
$pdo = connectDatabase();
echo "<!DOCTYPE html>
<html lang='zh-CN'>
<head>
<meta charset='UTF-8'>
<title>VIP客户数据验证</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }
.container { max-width: 1200px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
h1, h2 { color: #333; }
.table { width: 100%; border-collapse: collapse; margin: 20px 0; }
.table th, .table td { border: 1px solid #ddd; padding: 12px; text-align: left; }
.table th { background-color: #f2f2f2; font-weight: bold; }
.table tr:nth-child(even) { background-color: #f9f9f9; }
.success { color: #28a745; font-weight: bold; }
.error { color: #dc3545; font-weight: bold; }
.warning { color: #ffc107; font-weight: bold; }
.info { color: #17a2b8; }
.query-box { background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 4px; padding: 15px; margin: 10px 0; }
.count-box { background: #e9ecef; border-radius: 4px; padding: 15px; margin: 10px 0; text-align: center; }
.count-number { font-size: 24px; font-weight: bold; color: #007bff; }
</style>
</head>
<body>
<div class='container'>
<h1>🔍 VIP客户数据验证报告</h1>";
// 1. 检查VIP客户表是否存在
echo "<h2>1. 数据库表检查</h2>";
try {
$stmt = $pdo->query("SHOW TABLES LIKE 'vip_customers'");
$tableExists = $stmt->rowCount() > 0;
if ($tableExists) {
echo "<p class='success'>✅ vip_customers 表存在</p>";
// 获取表结构
$stmt = $pdo->query("DESCRIBE vip_customers");
$columns = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo "<h3>表结构:</h3>";
echo "<table class='table'>";
echo "<tr><th>字段名</th><th>类型</th><th>是否为空</th><th>键</th><th>默认值</th><th>额外</th></tr>";
foreach ($columns as $column) {
echo "<tr>";
echo "<td>{$column['Field']}</td>";
echo "<td>{$column['Type']}</td>";
echo "<td>{$column['Null']}</td>";
echo "<td>{$column['Key']}</td>";
echo "<td>{$column['Default']}</td>";
echo "<td>{$column['Extra']}</td>";
echo "</tr>";
}
echo "</table>";
} else {
echo "<p class='error'>❌ vip_customers 表不存在</p>";
}
} catch (Exception $e) {
echo "<p class='error'>❌ 表检查失败: " . $e->getMessage() . "</p>";
}
// 2. VIP客户总数统计
echo "<h2>2. VIP客户统计</h2>";
try {
// 总数
$stmt = $pdo->query("SELECT COUNT(*) as total FROM vip_customers");
$totalResult = $stmt->fetch(PDO::FETCH_ASSOC);
$totalCount = $totalResult['total'];
echo "<div class='count-box'>";
echo "<p>VIP客户总数</p>";
echo "<div class='count-number'>$totalCount</div>";
echo "</div>";
// 活跃状态统计
$stmt = $pdo->query("SELECT is_active, COUNT(*) as count FROM vip_customers GROUP BY is_active");
$activeStats = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo "<h3>按状态统计:</h3>";
echo "<table class='table'>";
echo "<tr><th>状态</th><th>数量</th><th>说明</th></tr>";
foreach ($activeStats as $stat) {
$status = $stat['is_active'] ? '活跃' : '非活跃';
$class = $stat['is_active'] ? 'success' : 'warning';
echo "<tr class='$class'>";
echo "<td>$status</td>";
echo "<td>{$stat['count']}</td>";
echo "<td>" . ($stat['is_active'] ? '可搜索' : '不可搜索') . "</td>";
echo "</tr>";
}
echo "</table>";
} catch (Exception $e) {
echo "<p class='error'>❌ 统计查询失败: " . $e->getMessage() . "</p>";
}
// 3. 活跃VIP客户详细数据
echo "<h2>3. 活跃VIP客户详细数据</h2>";
try {
$stmt = $pdo->query("SELECT * FROM vip_customers WHERE is_active = 1 ORDER BY created_at DESC");
$vipCustomers = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (count($vipCustomers) > 0) {
echo "<p class='success'>✅ 找到 " . count($vipCustomers) . " 个活跃VIP客户</p>";
echo "<table class='table'>";
echo "<tr>";
echo "<th>ID</th>";
echo "<th>客户姓名</th>";
echo "<th>手机号</th>";
echo "<th>邮箱</th>";
echo "<th>车型</th>";
echo "<th>车牌号</th>";
echo "<th>注册时间</th>";
echo "</tr>";
foreach ($vipCustomers as $customer) {
echo "<tr>";
echo "<td>{$customer['id']}</td>";
echo "<td><strong>{$customer['customer_name']}</strong></td>";
echo "<td>{$customer['phone']}</td>";
echo "<td>{$customer['email']}</td>";
echo "<td>{$customer['car_model']}</td>";
echo "<td>{$customer['car_number']}</td>";
echo "<td>{$customer['created_at']}</td>";
echo "</tr>";
}
echo "</table>";
// 显示原始JSON数据
echo "<h3>原始JSON数据 (API返回格式):</h3>";
echo "<div class='query-box'>";
echo "<pre>" . json_encode($vipCustomers, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "</pre>";
echo "</div>";
} else {
echo "<p class='warning'>⚠️ 没有找到活跃的VIP客户</p>";
}
} catch (Exception $e) {
echo "<p class='error'>❌ 查询VIP客户失败: " . $e->getMessage() . "</p>";
}
// 4. 搜索功能测试
echo "<h2>4. 搜索功能测试</h2>";
if (isset($vipCustomers) && count($vipCustomers) > 0) {
echo "<p>基于实际数据进行搜索测试:</p>";
// 测试各种搜索词
$testQueries = [
'姓名首字' => substr($vipCustomers[0]['customer_name'], 0, 1),
'姓名中字' => mb_substr($vipCustomers[0]['customer_name'], 1, 1, 'UTF-8'),
'手机前3位' => substr($vipCustomers[0]['phone'], 0, 3),
'手机后4位' => substr($vipCustomers[0]['phone'], -4),
'姓氏' => '张',
'号段' => '139'
];
foreach ($testQueries as $description => $query) {
echo "<div class='query-box'>";
echo "<strong>测试: $description (搜索词: '$query')</strong><br>";
$results = [];
foreach ($vipCustomers as $customer) {
$nameMatch = mb_stripos($customer['customer_name'], $query, 0, 'UTF-8') !== false;
$phoneMatch = stripos($customer['phone'], $query) !== false;
if ($nameMatch || $phoneMatch) {
$results[] = $customer['customer_name'] . ' (' . $customer['phone'] . ')';
}
}
if (count($results) > 0) {
echo "<span class='success'>✅ 找到 " . count($results) . " 个结果:</span><br>";
foreach ($results as $result) {
echo "$result<br>";
}
} else {
echo "<span class='warning'>⚠️ 未找到匹配结果</span>";
}
echo "</div>";
}
} else {
echo "<p class='warning'>⚠️ 没有VIP客户数据可供测试</p>";
}
// 5. API端点测试
echo "<h2>5. API端点测试</h2>";
echo "<p>测试 get_vip_customers.php 接口:</p>";
if (file_exists('get_vip_customers.php')) {
echo "<p class='success'>✅ get_vip_customers.php 文件存在</p>";
// 模拟API调用
ob_start();
include 'get_vip_customers.php';
$apiOutput = ob_get_clean();
echo "<div class='query-box'>";
echo "<strong>API输出:</strong><br>";
echo "<pre>" . htmlspecialchars($apiOutput) . "</pre>";
echo "</div>";
// 尝试解析JSON
try {
$apiData = json_decode($apiOutput, true);
if (json_last_error() === JSON_ERROR_NONE) {
echo "<p class='success'>✅ API返回有效JSON数据</p>";
echo "<p>数据记录数: " . (is_array($apiData) ? count($apiData) : 'N/A') . "</p>";
} else {
echo "<p class='error'>❌ API返回无效JSON: " . json_last_error_msg() . "</p>";
}
} catch (Exception $e) {
echo "<p class='error'>❌ JSON解析失败: " . $e->getMessage() . "</p>";
}
} else {
echo "<p class='error'>❌ get_vip_customers.php 文件不存在</p>";
}
echo "</div></body></html>";
?>
+490
View File
@@ -0,0 +1,490 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VIP客户搜索调试工具</title>
<style>
body {
font-family: 'Consolas', monospace;
margin: 20px;
background: #1e1e1e;
color: #d4d4d4;
}
.debug-panel {
background: #2d2d30;
border: 1px solid #3e3e42;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
.debug-title {
color: #4ec9b0;
font-size: 18px;
font-weight: bold;
margin-bottom: 15px;
border-bottom: 2px solid #4ec9b0;
padding-bottom: 5px;
}
.test-button {
background: #0078d4;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
margin: 5px;
font-family: 'Consolas', monospace;
}
.test-button:hover {
background: #106ebe;
}
.test-button.success {
background: #107c10;
}
.test-button.error {
background: #d83b01;
}
.output {
background: #1e1e1e;
border: 1px solid #3e3e42;
border-radius: 4px;
padding: 15px;
margin: 10px 0;
font-family: 'Consolas', monospace;
white-space: pre-wrap;
max-height: 400px;
overflow-y: auto;
}
.output.success {
border-left: 4px solid #107c10;
}
.output.error {
border-left: 4px solid #d83b01;
}
.output.info {
border-left: 4px solid #0078d4;
}
.search-input {
background: #3c3c3c;
border: 1px solid #5a5a5a;
color: #d4d4d4;
padding: 10px;
border-radius: 4px;
width: 300px;
margin: 10px 5px;
font-family: 'Consolas', monospace;
}
.status-indicator {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 8px;
}
.status-loading { background: #ff8c00; }
.status-success { background: #107c10; }
.status-error { background: #d83b01; }
.status-warning { background: #ff8c00; }
.customer-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 10px;
margin: 15px 0;
}
.customer-card {
background: #252526;
border: 1px solid #3e3e42;
border-radius: 4px;
padding: 10px;
}
.customer-name {
color: #4ec9b0;
font-weight: bold;
font-size: 14px;
}
.customer-phone {
color: #d4d4d4;
font-size: 12px;
margin: 2px 0;
}
.customer-car {
color: #9cdcfe;
font-size: 11px;
margin: 2px 0;
}
.json-viewer {
background: #1e1e1e;
border: 1px solid #3e3e42;
border-radius: 4px;
padding: 10px;
margin: 10px 0;
font-family: 'Consolas', monospace;
font-size: 12px;
}
</style>
</head>
<body>
<div class="debug-panel">
<div class="debug-title">🔧 VIP客户搜索调试工具</div>
<!-- 数据库连接测试 -->
<div>
<h3><span class="status-indicator status-loading" id="db-status"></span>数据库连接测试</h3>
<button class="test-button" onclick="testDatabaseConnection()">测试数据库连接</button>
<div id="db-output" class="output" style="display: none;"></div>
</div>
<!-- VIP数据加载测试 -->
<div>
<h3><span class="status-indicator status-loading" id="data-status"></span>VIP数据加载测试</h3>
<button class="test-button" onclick="testVIPDataLoading()">加载VIP客户数据</button>
<div id="data-output" class="output" style="display: none;"></div>
<div id="customers-display" style="display: none;"></div>
</div>
<!-- 搜索功能测试 -->
<div>
<h3><span class="status-indicator status-loading" id="search-status"></span>搜索功能测试</h3>
<div style="margin: 10px 0;">
<input type="text" id="debug-search-input" class="search-input"
placeholder="输入搜索关键词" oninput="testSearch()">
<button class="test-button" onclick="testSearch()">搜索</button>
<button class="test-button" onclick="clearSearch()">清除</button>
</div>
<div id="search-output" class="output" style="display: none;"></div>
<div id="search-results" class="customer-list" style="display: none;"></div>
</div>
<!-- 完整流程测试 -->
<div>
<h3><span class="status-indicator status-loading" id="flow-status"></span>完整流程测试</h3>
<button class="test-button" onclick="testFullFlow()">运行完整流程测试</button>
<div id="flow-output" class="output" style="display: none;"></div>
</div>
<!-- 实时监控 -->
<div>
<h3><span class="status-indicator status-success" id="monitor-status"></span>实时监控</h3>
<div id="monitor-output" class="output"></div>
</div>
</div>
<script>
let vipCustomers = [];
let debugLog = [];
// 添加调试日志
function addLog(message, type = 'info') {
const timestamp = new Date().toLocaleTimeString();
const logMessage = `[${timestamp}] ${message}`;
debugLog.push(logMessage);
updateMonitor();
console.log(logMessage);
}
// 更新监控显示
function updateMonitor() {
const monitorOutput = document.getElementById('monitor-output');
monitorOutput.textContent = debugLog.join('\n');
monitorOutput.scrollTop = monitorOutput.scrollHeight;
}
// 设置状态指示器
function setStatus(elementId, status) {
const indicator = document.getElementById(elementId);
indicator.className = `status-indicator status-${status}`;
}
// 显示输出
function showOutput(elementId, content, type = 'info') {
const output = document.getElementById(elementId);
output.className = `output ${type}`;
output.textContent = content;
output.style.display = 'block';
}
// 测试数据库连接
async function testDatabaseConnection() {
addLog('开始测试数据库连接...');
setStatus('db-status', 'loading');
try {
const response = await fetch('get_vip_customers.php');
addLog(`API响应状态: ${response.status} ${response.statusText}`);
addLog(`响应头: ${JSON.stringify([...response.headers.entries()])}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const text = await response.text();
addLog(`原始响应: ${text.substring(0, 200)}${text.length > 200 ? '...' : ''}`);
if (text.trim().startsWith('{') || text.trim().startsWith('[')) {
try {
const data = JSON.parse(text);
addLog('JSON解析成功', 'success');
setStatus('db-status', 'success');
showOutput('db-output', `数据库连接成功!\n状态码: ${response.status}\n数据格式: ${Array.isArray(data) ? '数组' : '对象'}\n记录数: ${Array.isArray(data) ? data.length : 'N/A'}`, 'success');
} catch (e) {
addLog(`JSON解析失败: ${e.message}`, 'error');
setStatus('db-status', 'error');
showOutput('db-output', `JSON解析失败: ${e.message}\n原始内容: ${text}`, 'error');
}
} else {
setStatus('db-status', 'error');
showOutput('db-output', `响应不是JSON格式:\n${text}`, 'error');
}
} catch (error) {
addLog(`数据库连接失败: ${error.message}`, 'error');
setStatus('db-status', 'error');
showOutput('db-output', `数据库连接失败:\n${error.message}`, 'error');
}
}
// 测试VIP数据加载
async function testVIPDataLoading() {
addLog('开始测试VIP数据加载...');
setStatus('data-status', 'loading');
try {
const response = await fetch('get_vip_customers.php');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const text = await response.text();
const data = JSON.parse(text);
if (Array.isArray(data)) {
vipCustomers = data;
addLog(`成功加载 ${data.length} 条VIP客户数据`, 'success');
setStatus('data-status', 'success');
showOutput('data-output', `✅ 数据加载成功!\n总计: ${data.length} 条记录\n\n详细数据:\n${JSON.stringify(data, null, 2)}`, 'success');
// 显示客户列表
displayCustomers(data);
// 测试搜索功能
testSearchLogic();
} else {
setStatus('data-status', 'error');
showOutput('data-output', `❌ 数据格式错误: ${JSON.stringify(data)}`, 'error');
}
} catch (error) {
addLog(`VIP数据加载失败: ${error.message}`, 'error');
setStatus('data-status', 'error');
showOutput('data-output', `❌ 数据加载失败: ${error.message}`, 'error');
}
}
// 显示客户列表
function displayCustomers(customers) {
const displayDiv = document.getElementById('customers-display');
if (customers.length === 0) {
displayDiv.innerHTML = '<p>没有找到VIP客户数据</p>';
displayDiv.style.display = 'block';
return;
}
let html = `<h4>VIP客户列表 (${customers.length}条):</h4><div class="customer-list">`;
customers.forEach((customer, index) => {
html += `
<div class="customer-card">
<div class="customer-name">${index + 1}. ${customer.customer_name}</div>
<div class="customer-phone">📞 ${customer.phone}</div>
<div class="customer-car">🚗 ${customer.car_model || '未知'} - ${customer.car_number || '未知'}</div>
<div class="customer-email">✉️ ${customer.email || '无邮箱'}</div>
</div>
`;
});
html += '</div>';
displayDiv.innerHTML = html;
displayDiv.style.display = 'block';
}
// 测试搜索逻辑
function testSearchLogic() {
addLog('测试搜索逻辑...');
if (vipCustomers.length === 0) {
addLog('没有VIP客户数据可搜索', 'warning');
return;
}
// 测试不同搜索词
const testTerms = [
vipCustomers[0].customer_name.substring(0, 1), // 名字第一个字
vipCustomers[0].phone.substring(0, 3), // 电话前3位
'张', // 中文名测试
'139' // 号段测试
];
testTerms.forEach(term => {
const results = searchCustomers(vipCustomers, term);
addLog(`搜索"${term}": 找到 ${results.length} 个结果`, results.length > 0 ? 'success' : 'warning');
});
}
// 搜索客户
function searchCustomers(customers, term) {
if (!term || term.trim() === '') return [];
const searchTerm = term.toLowerCase().trim();
return customers.filter(customer => {
const name = (customer.customer_name || '').toLowerCase();
const phone = (customer.phone || '');
return name.includes(searchTerm) || phone.includes(searchTerm);
});
}
// 测试搜索
function testSearch() {
const searchTerm = document.getElementById('debug-search-input').value.trim();
if (searchTerm === '') {
document.getElementById('search-results').style.display = 'none';
document.getElementById('search-output').style.display = 'none';
return;
}
addLog(`开始搜索: "${searchTerm}"`);
setStatus('search-status', 'loading');
const results = searchCustomers(vipCustomers, searchTerm);
addLog(`搜索"${searchTerm}"完成,找到 ${results.length} 个结果`);
setStatus('search-status', results.length > 0 ? 'success' : 'warning');
if (results.length === 0) {
showOutput('search-output', `❌ 搜索"${searchTerm}"未找到匹配结果\n\nVIP客户总数: ${vipCustomers.length}\n\n可用的搜索测试:\n• 姓名: 张、王、李\n• 手机号: 13900139001、13900139002、13900139003`, 'warning');
document.getElementById('search-results').style.display = 'none';
} else {
showOutput('search-output', `✅ 搜索"${searchTerm}"找到 ${results.length} 个结果:\n\n${results.map(r => `${r.customer_name} (${r.phone})`).join('\n')}`, 'success');
displaySearchResults(results, searchTerm);
}
}
// 显示搜索结果
function displaySearchResults(results, searchTerm) {
const resultsDiv = document.getElementById('search-results');
let html = `<h4>搜索结果 (${results.length}条):</h4>`;
results.forEach((customer, index) => {
// 高亮关键词
const highlightName = highlightText(customer.customer_name, searchTerm);
const highlightPhone = highlightText(customer.phone, searchTerm);
html += `
<div class="customer-card">
<div class="customer-name">${highlightName}</div>
<div class="customer-phone">📞 ${highlightPhone}</div>
<div class="customer-car">🚗 ${customer.car_model || '未知'} - ${customer.car_number || '未知'}</div>
</div>
`;
});
resultsDiv.innerHTML = html;
resultsDiv.style.display = 'block';
}
// 高亮文本
function highlightText(text, searchTerm) {
if (!text || !searchTerm) return text || '';
const regex = new RegExp(`(${searchTerm})`, 'gi');
return text.replace(regex, '<mark style="background: yellow; color: black;">$1</mark>');
}
// 清除搜索
function clearSearch() {
document.getElementById('debug-search-input').value = '';
document.getElementById('search-results').style.display = 'none';
document.getElementById('search-output').style.display = 'none';
addLog('清除搜索结果');
}
// 测试完整流程
async function testFullFlow() {
addLog('开始完整流程测试...');
setStatus('flow-status', 'loading');
let steps = [];
try {
// 步骤1: 测试数据库连接
steps.push('1. 数据库连接测试');
const response = await fetch('get_vip_customers.php');
if (!response.ok) throw new Error('数据库连接失败');
// 步骤2: 加载数据
steps.push('2. VIP数据加载测试');
const data = JSON.parse(await response.text());
if (!Array.isArray(data)) throw new Error('数据格式错误');
vipCustomers = data;
// 步骤3: 搜索测试
steps.push('3. 搜索功能测试');
const testResults = searchCustomers(vipCustomers, '张');
if (testResults.length === 0) throw new Error('搜索功能异常');
steps.push('4. 数据显示测试');
setStatus('flow-status', 'success');
showOutput('flow-output', `✅ 完整流程测试通过!\n\n执行的步骤:\n${steps.join('\n')}\n\nVIP客户总数: ${vipCustomers.length}\n搜索测试结果: ${testResults.length}`, 'success');
addLog('完整流程测试通过', 'success');
} catch (error) {
setStatus('flow-status', 'error');
showOutput('flow-output', `❌ 完整流程测试失败!\n\n已执行步骤:\n${steps.join('\n')}\n\n错误信息: ${error.message}`, 'error');
addLog(`完整流程测试失败: ${error.message}`, 'error');
}
}
// 页面加载完成后自动运行测试
window.addEventListener('load', function() {
addLog('VIP搜索调试工具加载完成');
// 3秒后自动开始测试
setTimeout(() => {
testDatabaseConnection();
setTimeout(() => {
testVIPDataLoading();
}, 2000);
}, 3000);
});
</script>
</body>
</html>