完善查询

This commit is contained in:
2025-08-02 22:07:43 +08:00
parent eae6a14272
commit c3fba1b248
3 changed files with 421 additions and 7 deletions

View File

@@ -718,6 +718,13 @@ function getShardingConfig() {
enabled: document.getElementById('enableSharding').checked,
use_sharding_for_pro: document.getElementById('use_sharding_for_pro').checked,
use_sharding_for_test: document.getElementById('use_sharding_for_test').checked,
// 生产环境分表参数
pro_interval_seconds: parseInt(document.getElementById('pro_interval_seconds').value) || 604800,
pro_table_count: parseInt(document.getElementById('pro_table_count').value) || 14,
// 测试环境分表参数
test_interval_seconds: parseInt(document.getElementById('test_interval_seconds').value) || 604800,
test_table_count: parseInt(document.getElementById('test_table_count').value) || 14,
// 保持向后兼容的通用参数
interval_seconds: parseInt(document.getElementById('pro_interval_seconds').value) || 604800,
table_count: parseInt(document.getElementById('pro_table_count').value) || 14
}
@@ -736,6 +743,11 @@ function displayResults(results) {
document.getElementById('diff-count').textContent = results.differences.length;
document.getElementById('identical-count').textContent = results.identical_results.length;
// 计算原始数据总数
const totalRawData = (results.raw_pro_data ? results.raw_pro_data.length : 0) +
(results.raw_test_data ? results.raw_test_data.length : 0);
document.getElementById('rawdata-count').textContent = totalRawData;
// 初始化相同结果分页数据
filteredIdenticalResults = results.identical_results;
currentIdenticalPage = 1;
@@ -747,6 +759,7 @@ function displayResults(results) {
// 显示各个面板内容
displayDifferences();
displayIdenticalResults();
displayRawData(results);
displayComparisonSummary(results.summary);
// 显示结果区域
@@ -3202,4 +3215,353 @@ function autoRefreshLogsAfterQuery() {
setTimeout(() => {
refreshQueryLogs();
}, 500);
}
// 原始数据相关变量
let currentRawData = null;
let filteredRawData = [];
let currentRawDataPage = 1;
let rawDataPageSize = 20;
// 显示原始数据
function displayRawData(results) {
currentRawData = results;
// 添加调试信息
console.log('displayRawData - results.query_config:', results.query_config);
console.log('displayRawData - 当前数据键:', Object.keys(results));
// 合并生产和测试环境数据
const combinedData = [];
if (results.raw_pro_data) {
results.raw_pro_data.forEach(record => {
combinedData.push({
environment: 'production',
data: record,
displayName: '生产环境'
});
});
}
if (results.raw_test_data) {
results.raw_test_data.forEach(record => {
combinedData.push({
environment: 'test',
data: record,
displayName: '测试环境'
});
});
}
// 按环境排序,生产环境在前
combinedData.sort((a, b) => {
if (a.environment === 'production' && b.environment === 'test') return -1;
if (a.environment === 'test' && b.environment === 'production') return 1;
return 0;
});
filteredRawData = combinedData;
currentRawDataPage = 1;
renderRawDataContent();
}
// 渲染原始数据内容
function renderRawDataContent() {
const container = document.getElementById('raw-data-content');
if (!filteredRawData.length) {
container.innerHTML = '<div class="alert alert-info">没有原始数据可显示</div>';
return;
}
// 分页计算
const totalPages = Math.ceil(filteredRawData.length / rawDataPageSize);
const startIndex = (currentRawDataPage - 1) * rawDataPageSize;
const endIndex = startIndex + rawDataPageSize;
const currentPageData = filteredRawData.slice(startIndex, endIndex);
let html = `
<!-- 分页控制 -->
<div class="row mb-3">
<div class="col-md-6">
<div class="d-flex align-items-center">
<label class="form-label me-2 mb-0">每页显示:</label>
<select class="form-select form-select-sm me-2" style="width: auto;" onchange="changeRawDataPageSize(this.value)">
<option value="10" ${rawDataPageSize == 10 ? 'selected' : ''}>10条</option>
<option value="20" ${rawDataPageSize == 20 ? 'selected' : ''}>20条</option>
<option value="50" ${rawDataPageSize == 50 ? 'selected' : ''}>50条</option>
<option value="100" ${rawDataPageSize == 100 ? 'selected' : ''}>100条</option>
</select>
<span class="ms-2 text-muted">共 ${filteredRawData.length} 条记录</span>
</div>
</div>
<div class="col-md-6">
<div class="d-flex justify-content-end align-items-center">
${generateRawDataPagination(currentRawDataPage, totalPages)}
</div>
</div>
</div>
`;
// 显示数据记录
currentPageData.forEach((item, index) => {
const globalIndex = startIndex + index + 1;
const envBadgeClass = item.environment === 'production' ? 'bg-primary' : 'bg-success';
// 获取主键值用于标识 - 修复主键解析问题
let keyValue = 'N/A';
// 从当前配置获取keys因为query_config可能不在results中
const currentConfig = getCurrentConfig();
console.log('渲染原始数据 - currentConfig.query_config.keys:', currentConfig.query_config.keys);
console.log('渲染原始数据 - item.data keys:', Object.keys(item.data));
if (currentConfig && currentConfig.query_config && currentConfig.query_config.keys && currentConfig.query_config.keys.length > 0) {
const keyFields = currentConfig.query_config.keys;
const keyValues = keyFields.map(key => {
const value = item.data[key];
console.log(`查找主键字段 ${key}: `, value);
return value || 'N/A';
}).filter(val => val !== 'N/A');
if (keyValues.length > 0) {
keyValue = keyValues.join(', ');
}
} else {
// 如果没有配置主键,尝试常见的主键字段名
const commonKeyFields = ['id', 'statusid', 'key', 'uuid'];
for (const field of commonKeyFields) {
if (item.data[field] !== undefined) {
keyValue = item.data[field];
break;
}
}
}
// 格式化JSON数据
const jsonData = JSON.stringify(item.data, null, 2);
html += `
<div class="card mb-3 border-${item.environment === 'production' ? 'primary' : 'success'}">
<div class="card-header bg-light">
<div class="row align-items-center">
<div class="col">
<strong>记录 #${globalIndex}</strong>
<span class="badge ${envBadgeClass} ms-2">${item.displayName}</span>
</div>
<div class="col-auto">
<button class="btn btn-sm btn-outline-secondary me-2" type="button"
data-bs-toggle="collapse" data-bs-target="#rawCollapse${globalIndex}">
<i class="fas fa-eye"></i> 查看详情
</button>
<button class="btn btn-sm btn-outline-info" onclick='copyRawRecord(${JSON.stringify(JSON.stringify(item.data))})'>
<i class="fas fa-copy"></i> 复制
</button>
</div>
</div>
<p class="mb-0 mt-2"><strong>主键:</strong> ${keyValue}</p>
</div>
<div class="collapse" id="rawCollapse${globalIndex}">
<div class="card-body">
<pre class="bg-light p-3 rounded" style="max-height: 500px; overflow-y: auto; font-size: 0.9em; line-height: 1.4;">${escapeHtml(jsonData)}</pre>
</div>
</div>
</div>
`;
});
// 底部分页
if (totalPages > 1) {
html += `
<div class="row mt-3">
<div class="col-12">
<div class="d-flex justify-content-center">
${generateRawDataPagination(currentRawDataPage, totalPages)}
</div>
</div>
</div>
`;
}
container.innerHTML = html;
}
// 生成原始数据分页导航
function generateRawDataPagination(currentPage, totalPages) {
if (totalPages <= 1) return '';
let html = '<nav><ul class="pagination pagination-sm mb-0">';
// 上一页
html += `
<li class="page-item ${currentPage === 1 ? 'disabled' : ''}">
<a class="page-link" href="#" onclick="goToRawDataPage(${currentPage - 1})">
<i class="fas fa-chevron-left"></i>
</a>
</li>
`;
// 页码
const startPage = Math.max(1, currentPage - 2);
const endPage = Math.min(totalPages, currentPage + 2);
if (startPage > 1) {
html += '<li class="page-item"><a class="page-link" href="#" onclick="goToRawDataPage(1)">1</a></li>';
if (startPage > 2) {
html += '<li class="page-item disabled"><span class="page-link">...</span></li>';
}
}
for (let i = startPage; i <= endPage; i++) {
html += `
<li class="page-item ${i === currentPage ? 'active' : ''}">
<a class="page-link" href="#" onclick="goToRawDataPage(${i})">${i}</a>
</li>
`;
}
if (endPage < totalPages) {
if (endPage < totalPages - 1) {
html += '<li class="page-item disabled"><span class="page-link">...</span></li>';
}
html += `<li class="page-item"><a class="page-link" href="#" onclick="goToRawDataPage(${totalPages})">${totalPages}</a></li>`;
}
// 下一页
html += `
<li class="page-item ${currentPage === totalPages ? 'disabled' : ''}">
<a class="page-link" href="#" onclick="goToRawDataPage(${currentPage + 1})">
<i class="fas fa-chevron-right"></i>
</a>
</li>
`;
html += '</ul></nav>';
return html;
}
// 跳转到指定原始数据页面
function goToRawDataPage(page) {
const totalPages = Math.ceil(filteredRawData.length / rawDataPageSize);
if (page < 1 || page > totalPages) return;
currentRawDataPage = page;
renderRawDataContent();
}
// 改变原始数据每页显示数量
function changeRawDataPageSize(newSize) {
rawDataPageSize = parseInt(newSize);
currentRawDataPage = 1;
renderRawDataContent();
}
// 过滤原始数据(按环境)
function filterRawData() {
if (!currentRawData) return;
const showPro = document.getElementById('showProData').checked;
const showTest = document.getElementById('showTestData').checked;
let combinedData = [];
if (showPro && currentRawData.raw_pro_data) {
currentRawData.raw_pro_data.forEach(record => {
combinedData.push({
environment: 'production',
data: record,
displayName: '生产环境'
});
});
}
if (showTest && currentRawData.raw_test_data) {
currentRawData.raw_test_data.forEach(record => {
combinedData.push({
environment: 'test',
data: record,
displayName: '测试环境'
});
});
}
// 按环境排序
combinedData.sort((a, b) => {
if (a.environment === 'production' && b.environment === 'test') return -1;
if (a.environment === 'test' && b.environment === 'production') return 1;
return 0;
});
filteredRawData = combinedData;
currentRawDataPage = 1;
renderRawDataContent();
}
// 搜索原始数据
function searchRawData(searchTerm) {
if (!currentRawData) return;
const showPro = document.getElementById('showProData').checked;
const showTest = document.getElementById('showTestData').checked;
let combinedData = [];
if (showPro && currentRawData.raw_pro_data) {
currentRawData.raw_pro_data.forEach(record => {
combinedData.push({
environment: 'production',
data: record,
displayName: '生产环境'
});
});
}
if (showTest && currentRawData.raw_test_data) {
currentRawData.raw_test_data.forEach(record => {
combinedData.push({
environment: 'test',
data: record,
displayName: '测试环境'
});
});
}
if (searchTerm.trim()) {
const term = searchTerm.toLowerCase();
combinedData = combinedData.filter(item => {
// 搜索所有字段值
return Object.values(item.data).some(value =>
String(value).toLowerCase().includes(term)
);
});
}
// 按环境排序
combinedData.sort((a, b) => {
if (a.environment === 'production' && b.environment === 'test') return -1;
if (a.environment === 'test' && b.environment === 'production') return 1;
return 0;
});
filteredRawData = combinedData;
currentRawDataPage = 1;
renderRawDataContent();
}
// 复制原始记录
function copyRawRecord(recordStr) {
try {
const record = JSON.parse(recordStr);
const formattedData = JSON.stringify(record, null, 2);
navigator.clipboard.writeText(formattedData).then(() => {
showAlert('success', '原始记录已复制到剪贴板');
}).catch(err => {
console.error('复制失败:', err);
showAlert('danger', '复制失败,请手动选择复制');
});
} catch (error) {
showAlert('danger', '复制失败: ' + error.message);
}
}