Files
BigDataTool/templates/redis_compare.html
2025-08-04 09:14:27 +08:00

696 lines
35 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Redis集群比对工具</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style>
.config-section {
background-color: #f8f9fa;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
.result-section {
margin-top: 30px;
}
.difference-item {
border-left: 4px solid #dc3545;
padding-left: 15px;
margin-bottom: 15px;
background-color: #fff5f5;
padding: 15px;
border-radius: 5px;
}
.identical-item {
border-left: 4px solid #28a745;
padding-left: 15px;
margin-bottom: 15px;
background-color: #f8fff8;
padding: 15px;
border-radius: 5px;
}
.missing-item {
border-left: 4px solid #ffc107;
padding-left: 15px;
margin-bottom: 15px;
background-color: #fffbf0;
padding: 15px;
border-radius: 5px;
}
.stat-card {
text-align: center;
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
}
.loading {
display: none;
}
.query-keys {
min-height: 120px;
}
.redis-value {
font-family: 'Courier New', monospace;
font-size: 0.9em;
background-color: #f8f9fa !important;
white-space: pre-wrap;
word-break: break-all;
}
.node-input {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.node-input input {
margin-right: 10px;
}
.performance-section {
background: linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%);
border-radius: 10px;
padding: 20px;
margin-top: 20px;
}
.log-viewer {
background-color: #1e1e1e;
color: #d4d4d4;
font-family: 'Courier New', monospace;
font-size: 0.9em;
padding: 15px;
border-radius: 5px;
max-height: 400px;
overflow-y: auto;
}
.navbar {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
}
.breadcrumb {
background: none;
padding: 0;
}
.breadcrumb-item a {
color: #007bff;
text-decoration: none;
}
.btn-redis {
background: linear-gradient(135deg, #dc143c 0%, #b91c1c 100%);
border: none;
color: white;
}
.btn-redis:hover {
background: linear-gradient(135deg, #b91c1c 0%, #991b1b 100%);
color: white;
transform: translateY(-1px);
}
.redis-logo {
color: #dc143c;
}
.cluster-config {
border: 2px solid #e9ecef;
border-radius: 10px;
padding: 20px;
margin-bottom: 15px;
}
.cluster-config.active {
border-color: #dc143c;
background-color: #fff8f8;
}
</style>
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-dark">
<div class="container">
<a class="navbar-brand" href="/">
<i class="fas fa-database me-2"></i>
大数据工具集合
</a>
<div class="navbar-nav ms-auto">
<a class="nav-link" href="/">
<i class="fas fa-home"></i> 首页
</a>
<a class="nav-link" href="/db-compare">
<i class="fas fa-exchange-alt"></i> 数据库比对
</a>
<a class="nav-link active" href="/redis-compare">
<i class="fab fa-redis"></i> Redis比对
</a>
</div>
</div>
</nav>
<div class="container mt-4">
<!-- 面包屑导航 -->
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">首页</a></li>
<li class="breadcrumb-item active" aria-current="page">Redis集群比对工具</li>
</ol>
</nav>
<!-- 页面标题 -->
<div class="row mb-4">
<div class="col-12">
<div class="d-flex align-items-center">
<i class="fab fa-redis redis-logo fs-1 me-3"></i>
<div>
<h1 class="mb-1">Redis集群比对工具</h1>
<p class="text-muted mb-0">专业的Redis集群数据比对工具支持随机采样和指定Key查询</p>
</div>
</div>
</div>
</div>
<!-- 配置区域 -->
<div class="row">
<!-- 配置管理 -->
<div class="col-lg-12">
<div class="config-section mb-4">
<h4><i class="fas fa-cogs me-2"></i>配置管理</h4>
<div class="row">
<!-- 配置组管理 -->
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h6><i class="fas fa-layer-group me-2"></i>配置组管理</h6>
</div>
<div class="card-body">
<div class="row mb-3">
<div class="col-8">
<select class="form-select form-select-sm" id="redisConfigGroupSelect">
<option value="">选择Redis配置组...</option>
</select>
</div>
<div class="col-4">
<button class="btn btn-primary btn-sm w-100" onclick="loadSelectedRedisConfigGroup()">
<i class="fas fa-download"></i> 加载
</button>
</div>
</div>
<div class="row">
<div class="col-6">
<button class="btn btn-success btn-sm w-100" onclick="showSaveRedisConfigDialog()">
<i class="fas fa-save"></i> 保存配置
</button>
</div>
<div class="col-6">
<button class="btn btn-info btn-sm w-100" onclick="showManageRedisConfigDialog()">
<i class="fas fa-cog"></i> 管理配置
</button>
</div>
</div>
</div>
</div>
</div>
<!-- 查询历史 -->
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h6><i class="fas fa-history me-2"></i>查询历史</h6>
</div>
<div class="card-body">
<div class="row mb-3">
<div class="col-8">
<select class="form-select form-select-sm" id="redisHistorySelect">
<option value="">选择历史记录...</option>
</select>
</div>
<div class="col-4">
<button class="btn btn-warning btn-sm w-100" onclick="loadSelectedRedisHistory()">
<i class="fas fa-history"></i> 加载
</button>
</div>
</div>
<div class="row">
<div class="col-6">
<button class="btn btn-secondary btn-sm w-100" onclick="showSaveRedisHistoryDialog()">
<i class="fas fa-bookmark"></i> 保存历史
</button>
</div>
<div class="col-6">
<button class="btn btn-info btn-sm w-100" onclick="showManageRedisHistoryDialog()">
<i class="fas fa-list"></i> 管理历史
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 一键导入区域 -->
<div class="row mt-3">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h6><i class="fas fa-download me-2"></i>配置导入</h6>
</div>
<div class="card-body">
<button class="btn btn-outline-primary btn-sm me-2" onclick="showImportRedisConfigDialog('cluster1')">
<i class="fas fa-download"></i> 导入集群1配置
</button>
<button class="btn btn-outline-primary btn-sm" onclick="showImportRedisConfigDialog('cluster2')">
<i class="fas fa-download"></i> 导入集群2配置
</button>
<small class="form-text text-muted d-block mt-2">
支持YAML格式配置导入clusterName、clusterAddress、clusterPassword等
</small>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h6><i class="fas fa-file-alt me-2"></i>查询日志</h6>
</div>
<div class="card-body">
<button class="btn btn-outline-info btn-sm" onclick="showRedisQueryLogsDialog()">
<i class="fas fa-eye"></i> 查看查询日志
</button>
<small class="form-text text-muted d-block mt-2">
查看Redis比较操作的详细执行日志
</small>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Redis集群配置 -->
<div class="col-lg-12">
<div class="config-section">
<h4><i class="fas fa-server me-2"></i>Redis集群配置</h4>
<div class="row">
<!-- 第一个集群配置 -->
<div class="col-md-6">
<div class="cluster-config" id="cluster1-config">
<h5><i class="fas fa-database me-2"></i>集群1 (生产环境)</h5>
<div class="mb-3">
<label class="form-label">集群名称</label>
<input type="text" class="form-control" id="cluster1-name" value="生产集群" placeholder="输入集群名称">
</div>
<div class="mb-3">
<label class="form-label">节点列表</label>
<div id="cluster1-nodes">
<div class="node-input">
<input type="text" class="form-control node-host" placeholder="主机地址" value="127.0.0.1">
<input type="number" class="form-control node-port" placeholder="端口" value="7000" style="width: 100px;">
<button type="button" class="btn btn-outline-danger btn-sm remove-node">
<i class="fas fa-minus"></i>
</button>
</div>
</div>
<button type="button" class="btn btn-outline-primary btn-sm mt-2" onclick="addNode('cluster1')">
<i class="fas fa-plus"></i> 添加节点
</button>
</div>
<div class="mb-3">
<label class="form-label">密码 (可选)</label>
<input type="password" class="form-control" id="cluster1-password" placeholder="Redis密码">
</div>
<div class="row">
<div class="col-6">
<label class="form-label">连接超时 (秒)</label>
<input type="number" class="form-control" id="cluster1-timeout" value="3" min="1" max="30">
</div>
<div class="col-6">
<label class="form-label">最大连接数</label>
<input type="number" class="form-control" id="cluster1-max-conn" value="16" min="1" max="100">
</div>
</div>
<div class="mt-3">
<button type="button" class="btn btn-outline-success btn-sm" onclick="testConnection('cluster1')">
<i class="fas fa-plug"></i> 测试连接
</button>
</div>
</div>
</div>
<!-- 第二个集群配置 -->
<div class="col-md-6">
<div class="cluster-config" id="cluster2-config">
<h5><i class="fas fa-database me-2"></i>集群2 (测试环境)</h5>
<div class="mb-3">
<label class="form-label">集群名称</label>
<input type="text" class="form-control" id="cluster2-name" value="测试集群" placeholder="输入集群名称">
</div>
<div class="mb-3">
<label class="form-label">节点列表</label>
<div id="cluster2-nodes">
<div class="node-input">
<input type="text" class="form-control node-host" placeholder="主机地址" value="127.0.0.1">
<input type="number" class="form-control node-port" placeholder="端口" value="7001" style="width: 100px;">
<button type="button" class="btn btn-outline-danger btn-sm remove-node">
<i class="fas fa-minus"></i>
</button>
</div>
</div>
<button type="button" class="btn btn-outline-primary btn-sm mt-2" onclick="addNode('cluster2')">
<i class="fas fa-plus"></i> 添加节点
</button>
</div>
<div class="mb-3">
<label class="form-label">密码 (可选)</label>
<input type="password" class="form-control" id="cluster2-password" placeholder="Redis密码">
</div>
<div class="row">
<div class="col-6">
<label class="form-label">连接超时 (秒)</label>
<input type="number" class="form-control" id="cluster2-timeout" value="3" min="1" max="30">
</div>
<div class="col-6">
<label class="form-label">最大连接数</label>
<input type="number" class="form-control" id="cluster2-max-conn" value="16" min="1" max="100">
</div>
</div>
<div class="mt-3">
<button type="button" class="btn btn-outline-success btn-sm" onclick="testConnection('cluster2')">
<i class="fas fa-plug"></i> 测试连接
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 查询选项 -->
<div class="row">
<div class="col-lg-12">
<div class="config-section">
<h4><i class="fas fa-search me-2"></i>查询选项</h4>
<div class="mb-3">
<label class="form-label">查询模式</label>
<div class="form-check">
<input class="form-check-input" type="radio" name="queryMode" id="randomMode" value="random" checked>
<label class="form-check-label" for="randomMode">
<i class="fas fa-random me-1"></i>随机采样查询
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="queryMode" id="specifiedMode" value="specified">
<label class="form-check-label" for="specifiedMode">
<i class="fas fa-list me-1"></i>指定Key查询
</label>
</div>
</div>
<!-- 随机采样选项 -->
<div id="randomOptions">
<div class="row">
<div class="col-md-4">
<label class="form-label">采样数量</label>
<input type="number" class="form-control" id="sampleCount" value="100" min="1" max="10000">
</div>
<div class="col-md-4">
<label class="form-label">Key匹配模式</label>
<input type="text" class="form-control" id="keyPattern" value="*" placeholder="例如: user:*">
</div>
<div class="col-md-4">
<label class="form-label">数据源集群</label>
<select class="form-select" id="sourceCluster">
<option value="cluster1">集群1 (生产环境)</option>
<option value="cluster2" selected>集群2 (测试环境)</option>
</select>
</div>
</div>
</div>
<!-- 指定Key选项 -->
<div id="specifiedOptions" style="display: none;">
<div class="mb-3">
<label class="form-label">指定Key列表 (每行一个)</label>
<textarea class="form-control query-keys" id="specifiedKeys"
placeholder="请输入要查询的Key每行一个&#10;例如:&#10;user:1001&#10;user:1002&#10;session:abc123"></textarea>
</div>
</div>
</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="row">
<div class="col-12">
<div class="text-center">
<button type="button" class="btn btn-redis btn-lg me-3" onclick="executeRedisComparison()">
<i class="fas fa-play me-2"></i>开始比较
</button>
<button type="button" class="btn btn-outline-secondary btn-lg" onclick="clearResults()">
<i class="fas fa-eraser me-2"></i>清空结果
</button>
<div class="loading mt-3">
<div class="spinner-border text-danger me-2" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<span>正在执行Redis数据比较请稍候...</span>
</div>
</div>
</div>
</div>
<!-- 结果展示区域 -->
<div class="result-section" id="results" style="display: none;">
<!-- 统计卡片 -->
<div class="row" id="statsCards">
<!-- 统计卡片将通过JavaScript动态生成 -->
</div>
<!-- 详细结果 -->
<div class="row">
<div class="col-12">
<ul class="nav nav-tabs" id="resultTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="differences-tab" data-bs-toggle="tab" data-bs-target="#differences" type="button" role="tab">
<i class="fas fa-exclamation-triangle me-1"></i>差异数据 (<span id="diff-count">0</span>)
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="identical-tab" data-bs-toggle="tab" data-bs-target="#identical" type="button" role="tab">
<i class="fas fa-check-circle me-1"></i>相同数据 (<span id="identical-count">0</span>)
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="missing-tab" data-bs-toggle="tab" data-bs-target="#missing" type="button" role="tab">
<i class="fas fa-question-circle me-1"></i>缺失数据 (<span id="missing-count">0</span>)
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="performance-tab" data-bs-toggle="tab" data-bs-target="#performance" type="button" role="tab">
<i class="fas fa-chart-line me-1"></i>性能报告
</button>
</li>
</ul>
<div class="tab-content" id="resultTabContent">
<!-- 差异数据 -->
<div class="tab-pane fade show active" id="differences" role="tabpanel">
<div class="mt-3" id="differenceResults">
<!-- 差异结果将通过JavaScript动态生成 -->
</div>
</div>
<!-- 相同数据 -->
<div class="tab-pane fade" id="identical" role="tabpanel">
<div class="mt-3" id="identicalResults">
<!-- 相同结果将通过JavaScript动态生成 -->
</div>
</div>
<!-- 缺失数据 -->
<div class="tab-pane fade" id="missing" role="tabpanel">
<div class="mt-3" id="missingResults">
<!-- 缺失结果将通过JavaScript动态生成 -->
</div>
</div>
<!-- 性能报告 -->
<div class="tab-pane fade" id="performance" role="tabpanel">
<div class="performance-section mt-3" id="performanceReport">
<!-- 性能报告将通过JavaScript动态生成 -->
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Redis配置组保存模态框 -->
<div class="modal fade" id="saveRedisConfigModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-save me-2"></i>保存Redis配置组</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">配置组名称 *</label>
<input type="text" class="form-control" id="redisConfigGroupName" placeholder="输入配置组名称">
</div>
<div class="mb-3">
<label class="form-label">描述</label>
<textarea class="form-control" id="redisConfigGroupDescription" placeholder="输入配置组描述(可选)"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-success" onclick="saveRedisConfigGroup()">保存</button>
</div>
</div>
</div>
</div>
<!-- Redis配置管理模态框 -->
<div class="modal fade" id="manageRedisConfigModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-cog me-2"></i>Redis配置组管理</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div id="redisConfigGroupList">
<!-- 配置组列表将在这里动态生成 -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<!-- Redis配置导入模态框 -->
<div class="modal fade" id="importRedisConfigModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-download me-2"></i>导入Redis配置</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="alert alert-info">
<strong>支持的配置格式:</strong><br>
clusterName: "redis-test"<br>
clusterAddress: "10.20.2.109:6470"<br>
clusterPassword: ""<br>
cachePrefix: message.status.Writer.<br>
cacheTtl: 2000<br>
async: true
</div>
<div class="mb-3">
<label class="form-label">配置内容</label>
<textarea class="form-control" id="redisConfigImportText" rows="8"
placeholder="请粘贴Redis配置内容..."></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" onclick="importRedisConfig()">导入配置</button>
</div>
</div>
</div>
</div>
<!-- Redis查询历史保存模态框 -->
<div class="modal fade" id="saveRedisHistoryModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-bookmark me-2"></i>保存Redis查询历史</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">历史记录名称 *</label>
<input type="text" class="form-control" id="redisHistoryName" placeholder="输入历史记录名称">
</div>
<div class="mb-3">
<label class="form-label">描述</label>
<textarea class="form-control" id="redisHistoryDescription" placeholder="输入历史记录描述(可选)"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-success" onclick="saveRedisQueryHistory()">保存</button>
</div>
</div>
</div>
</div>
<!-- Redis查询历史管理模态框 -->
<div class="modal fade" id="manageRedisHistoryModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-list me-2"></i>Redis查询历史管理</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div id="redisHistoryList">
<!-- 查询历史列表将在这里动态生成 -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<!-- Redis查询日志模态框 -->
<div class="modal fade" id="redisQueryLogsModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-file-alt me-2"></i>Redis查询日志</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<h6 class="mb-0">Redis查询执行日志</h6>
<div>
<button class="btn btn-sm btn-outline-primary me-2" onclick="refreshRedisQueryLogs()">
<i class="fas fa-sync-alt"></i> 刷新
</button>
<button class="btn btn-sm btn-outline-danger" onclick="clearRedisQueryLogs()">
<i class="fas fa-trash"></i> 清空
</button>
</div>
</div>
<div id="redisQueryLogs" class="log-viewer">
<!-- 查询日志将在这里动态生成 -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<!-- 引入必要的JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="/static/js/redis_compare.js"></script>
</body>
</html>