diff --git a/app.py b/app.py index b380830..444adbd 100644 --- a/app.py +++ b/app.py @@ -319,6 +319,25 @@ def ensure_database(): conn.commit() logger.info("query_history表query_type字段添加成功") + # 添加查询结果数据存储字段 + if 'raw_results' not in history_column_names: + logger.info("添加raw_results字段到query_history表...") + cursor.execute("ALTER TABLE query_history ADD COLUMN raw_results TEXT") + conn.commit() + logger.info("query_history表raw_results字段添加成功") + + if 'differences_data' not in history_column_names: + logger.info("添加differences_data字段到query_history表...") + cursor.execute("ALTER TABLE query_history ADD COLUMN differences_data TEXT") + conn.commit() + logger.info("query_history表differences_data字段添加成功") + + if 'identical_data' not in history_column_names: + logger.info("添加identical_data字段到query_history表...") + cursor.execute("ALTER TABLE query_history ADD COLUMN identical_data TEXT") + conn.commit() + logger.info("query_history表identical_data字段添加成功") + conn.close() return True except Exception as e: @@ -672,8 +691,8 @@ def delete_config_group(group_id): def save_query_history(name, description, pro_config, test_config, query_config, query_keys, results_summary, execution_time, total_keys, differences_count, identical_count, - sharding_config=None, query_type='single'): - """保存查询历史记录,支持分表查询""" + sharding_config=None, query_type='single', raw_results=None, differences_data=None, identical_data=None): + """保存查询历史记录,支持分表查询和查询结果数据""" if not ensure_database(): logger.error("数据库初始化失败") return False @@ -686,8 +705,8 @@ def save_query_history(name, description, pro_config, test_config, query_config, INSERT INTO query_history (name, description, pro_config, test_config, query_config, query_keys, results_summary, execution_time, total_keys, differences_count, identical_count, - sharding_config, query_type) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + sharding_config, query_type, raw_results, differences_data, identical_data) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( name, description, json.dumps(pro_config), @@ -700,7 +719,10 @@ def save_query_history(name, description, pro_config, test_config, query_config, differences_count, identical_count, json.dumps(sharding_config) if sharding_config else None, - query_type + query_type, + json.dumps(raw_results) if raw_results else None, + json.dumps(differences_data) if differences_data else None, + json.dumps(identical_data) if identical_data else None )) conn.commit() logger.info(f"查询历史记录 '{name}' 保存成功,查询类型:{query_type}") @@ -731,6 +753,8 @@ def get_query_history(): history_list = [] for row in rows: + # 获取列名列表以检查字段是否存在 + column_names = [desc[0] for desc in cursor.description] history_list.append({ 'id': row['id'], 'name': row['name'], @@ -740,7 +764,7 @@ def get_query_history(): 'differences_count': row['differences_count'], 'identical_count': row['identical_count'], 'created_at': row['created_at'], - 'query_type': row.get('query_type', 'single') + 'query_type': row['query_type'] if 'query_type' in column_names else 'single' }) return history_list @@ -766,6 +790,8 @@ def get_query_history_by_id(history_id): row = cursor.fetchone() if row: + # 获取列名列表以检查字段是否存在 + column_names = [desc[0] for desc in cursor.description] return { 'id': row['id'], 'name': row['name'], @@ -781,8 +807,12 @@ def get_query_history_by_id(history_id): 'identical_count': row['identical_count'], 'created_at': row['created_at'], # 处理新字段,保持向后兼容 - 'sharding_config': json.loads(row['sharding_config']) if row.get('sharding_config') else None, - 'query_type': row.get('query_type', 'single') + 'sharding_config': json.loads(row['sharding_config']) if 'sharding_config' in column_names and row['sharding_config'] else None, + 'query_type': row['query_type'] if 'query_type' in column_names else 'single', + # 添加查询结果数据支持 + 'raw_results': json.loads(row['raw_results']) if 'raw_results' in column_names and row['raw_results'] else None, + 'differences_data': json.loads(row['differences_data']) if 'differences_data' in column_names and row['differences_data'] else None, + 'identical_data': json.loads(row['identical_data']) if 'identical_data' in column_names and row['identical_data'] else None } return None except Exception as e: @@ -1394,6 +1424,46 @@ def sharding_query_compare(): logger.info(f"分表比对完成:发现 {len(differences)} 处差异") + # 自动保存分表查询历史记录 + try: + # 生成历史记录名称 + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + history_name = f"分表查询_{timestamp}" + history_description = f"自动保存 - 分表查询{len(values)}个Key,发现{len(differences)}处差异" + + # 保存历史记录 + save_query_history( + name=history_name, + description=history_description, + pro_config=pro_config, + test_config=test_config, + query_config={ + 'keys': keys, + 'fields_to_compare': fields_to_compare, + 'exclude_fields': exclude_fields + }, + query_keys=values, + results_summary=summary, + execution_time=0.0, # 可以后续优化计算实际执行时间 + total_keys=len(values), + differences_count=len(differences), + identical_count=len(identical_results), + # 新增分表相关参数 + sharding_config=sharding_config, + query_type='sharding', + # 添加查询结果数据 + raw_results={ + 'raw_pro_data': [dict(row._asdict()) for row in pro_data] if pro_data else [], + 'raw_test_data': [dict(row._asdict()) for row in test_data] if test_data else [], + 'sharding_info': sharding_info # 包含分表信息 + }, + differences_data=differences, + identical_data=identical_results + ) + logger.info(f"分表查询历史记录保存成功: {history_name}") + except Exception as e: + logger.warning(f"保存分表查询历史记录失败: {e}") + # 结束查询批次 query_log_collector.end_current_batch() return jsonify(result) @@ -1529,7 +1599,14 @@ def query_compare(): execution_time=0.0, # 可以后续优化计算实际执行时间 total_keys=len(values), differences_count=len(differences), - identical_count=len(identical_results) + identical_count=len(identical_results), + # 添加查询结果数据 + raw_results={ + 'raw_pro_data': [dict(row._asdict()) for row in pro_data] if pro_data else [], + 'raw_test_data': [dict(row._asdict()) for row in test_data] if test_data else [] + }, + differences_data=differences, + identical_data=identical_results ) except Exception as e: logger.warning(f"保存查询历史记录失败: {e}") @@ -1691,6 +1768,56 @@ def api_get_query_history_detail(history_id): else: return jsonify({'success': False, 'error': '查询历史记录不存在'}), 404 +@app.route('/api/query-history//results', methods=['GET']) +def api_get_query_history_results(history_id): + """获取查询历史记录的完整结果数据""" + try: + history_record = get_query_history_by_id(history_id) + if not history_record: + return jsonify({'success': False, 'error': '历史记录不存在'}), 404 + + # 构建完整的查询结果格式,与API查询结果保持一致 + result = { + 'total_keys': history_record['total_keys'], + 'pro_count': len(history_record.get('raw_results', {}).get('raw_pro_data', [])) if history_record.get('raw_results') else 0, + 'test_count': len(history_record.get('raw_results', {}).get('raw_test_data', [])) if history_record.get('raw_results') else 0, + 'differences': history_record.get('differences_data', []), + 'identical_results': history_record.get('identical_data', []), + 'field_diff_count': {}, # 可以从differences_data中重新计算 + 'summary': history_record.get('results_summary', {}), + 'raw_pro_data': history_record.get('raw_results', {}).get('raw_pro_data', []) if history_record.get('raw_results') else [], + 'raw_test_data': history_record.get('raw_results', {}).get('raw_test_data', []) if history_record.get('raw_results') else [], + # 如果是分表查询,添加分表信息 + 'sharding_info': history_record.get('raw_results', {}).get('sharding_info') if history_record.get('raw_results') and history_record.get('query_type') == 'sharding' else None, + # 添加历史记录元信息 + 'history_info': { + 'id': history_record['id'], + 'name': history_record['name'], + 'description': history_record['description'], + 'created_at': history_record['created_at'], + 'query_type': history_record.get('query_type', 'single') + } + } + + # 重新计算field_diff_count + if history_record.get('differences_data'): + field_diff_count = {} + for diff in history_record['differences_data']: + if 'field' in diff: + field_name = diff['field'] + field_diff_count[field_name] = field_diff_count.get(field_name, 0) + 1 + result['field_diff_count'] = field_diff_count + + return jsonify({ + 'success': True, + 'data': result, + 'message': f'历史记录 "{history_record["name"]}" 结果加载成功' + }) + + except Exception as e: + logger.error(f"获取查询历史记录结果失败: {e}") + return jsonify({'success': False, 'error': f'获取历史记录结果失败: {str(e)}'}), 500 + @app.route('/api/query-history/', methods=['DELETE']) def api_delete_query_history(history_id): """删除查询历史记录""" diff --git a/static/js/app.js b/static/js/app.js index fa26cb1..6bf4d3a 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -2446,6 +2446,9 @@ async function showQueryHistoryDialog() { + @@ -2723,6 +2726,44 @@ async function viewHistoryDetail(historyId) { } } +// 加载历史记录结果 +async function loadHistoryResults(historyId) { + try { + const response = await fetch(`/api/query-history/${historyId}/results`); + const result = await response.json(); + + if (result.success) { + // 设置当前结果数据 + currentResults = result.data; + + // 根据查询类型设置分表模式 + if (result.data.history_info.query_type === 'sharding') { + isShardingMode = true; + document.getElementById('enableSharding').checked = true; + toggleShardingMode(); + } else { + isShardingMode = false; + document.getElementById('enableSharding').checked = false; + toggleShardingMode(); + } + + // 显示结果 + displayResults(result.data); + + // 关闭历史记录modal + const modal = bootstrap.Modal.getInstance(document.getElementById('queryHistoryModal')); + modal.hide(); + + const queryTypeDesc = result.data.history_info.query_type === 'sharding' ? '分表查询' : '单表查询'; + showAlert('success', `${queryTypeDesc}历史记录结果 "${result.data.history_info.name}" 加载成功`); + } else { + showAlert('danger', result.error || '加载历史记录结果失败'); + } + } catch (error) { + showAlert('danger', '加载历史记录结果失败: ' + error.message); + } +} + // 删除历史记录 async function deleteHistoryRecord(historyId, historyName) { if (!confirm(`确定要删除历史记录 "${historyName}" 吗?此操作不可撤销。`)) {