帮忙逐句解读和汇总解读作用
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
增强型源数据库管理器
扩展源数据库管理器,提供更多高级功能和优化的数据同步方法。
"""
import logging
import datetime
from typing import List, Dict, Any, Optional, Tuple, Union
import pymysql
from pymysql.cursors import DictCursor
from Colion.model.database.source_db_manager import SourceDatabaseManager
from Colion.model.database.db_manager import DatabaseManager
class EnhancedSourceDatabaseManager(SourceDatabaseManager):
"""
增强型源数据库管理器类
扩展源数据库管理器,提供更多高级功能和优化的数据同步方法。
"""
def __init__(self, host: str, port: int, user: str, password: str, database: str, charset: str = 'utf8mb4'):
"""
初始化增强型源数据库管理器
Args:
host: 数据库主机
port: 数据库端口
user: 数据库用户名
password: 数据库密码
database: 数据库名
charset: 数据库字符集,默认为utf8mb4
"""
super().__init__(host, port, user, password, database, charset)
self.logger = logging.getLogger('Colion.EnhancedSourceDatabaseManager')
self.logger.info("增强型源数据库管理器初始化完成")
def ensure_indexes(self, local_db: DatabaseManager):
"""
确保本地数据库中的索引存在
Args:
local_db: 本地数据库管理器
"""
try:
# 为query表创建索引
if local_db.table_exists('query'):
# 检查日期列是否存在
columns = local_db.query("PRAGMA table_info(query)")
column_names = [col['name'] for col in columns]
if '日期' in column_names:
local_db.execute("CREATE INDEX IF NOT EXISTS idx_query_date ON query(日期)")
self.logger.info("已确保query表的日期索引存在")
# 为work_order_list表创建索引
if local_db.table_exists('work_order_list'):
columns = local_db.query("PRAGMA table_info(work_order_list)")
column_names = [col['name'] for col in columns]
if 'createDate' in column_names:
local_db.execute("CREATE INDEX IF NOT EXISTS idx_work_order_createDate ON work_order_list(createDate)")
if 'id' in column_names:
local_db.execute("CREATE INDEX IF NOT EXISTS idx_work_order_id ON work_order_list(id)")
self.logger.info("已确保work_order_list表的索引存在")
# 为part_category表创建索引(检查列是否存在)
if local_db.table_exists('part_category'):
columns = local_db.query("PRAGMA table_info(part_category)")
column_names = [col['name'] for col in columns]
if 'part_code' in column_names:
local_db.execute("CREATE INDEX IF NOT EXISTS idx_part_category_code ON part_category(part_code)")
self.logger.info("已确保part_category表的索引存在")
else:
self.logger.info("part_category表中不存在part_code列,跳过索引创建")
# 为part_info表创建索引(检查列是否存在)
if local_db.table_exists('part_info'):
columns = local_db.query("PRAGMA table_info(part_info)")
column_names = [col['name'] for col in columns]
if 'part_code' in column_names:
local_db.execute("CREATE INDEX IF NOT EXISTS idx_part_info_code ON part_info(part_code)")
self.logger.info("已确保part_info表的索引存在")
else:
self.logger.info("part_info表中不存在part_code列,跳过索引创建")
# 为view_sop_progress表创建索引(如果需要的话)
if local_db.table_exists('view_sop_progress'):
self.logger.info("已确保view_sop_progress表存在")
except Exception as e:
self.logger.error(f"确保索引存在失败: {str(e)}", exc_info=True)
def sync_query_max_date(self, local_db: DatabaseManager) -> Dict[str, Any]:
"""
同步query表中日期字段最大值对应的行
Args:
local_db: 本地数据库管理器
Returns:
同步结果
"""
try:
self.logger.info("开始同步query表中日期字段最大值对应的行")
# 确保连接有效
if not self.conn:
self.connect()
# 获取源表结构
source_columns = self.get_table_columns('query')
if not source_columns:
self.logger.error("获取源表结构失败")
return {'status': '失败', 'message': '获取源表结构失败', 'row_count': 0}
# 检查本地表是否存在,如果不存在则创建
if not local_db.table_exists('query'):
self.logger.info("本地表不存在,创建表")
local_db.create_table('query', source_columns)
else:
# 检查是否需要添加新列
local_columns = local_db.get_table_info('query')
local_column_names = [col['name'] for col in local_columns]
new_columns = [col for col in source_columns if col['name'] not in local_column_names]
if new_columns:
self.logger.info(f"添加 {len(new_columns)} 个新列到本地表")
local_db.add_columns('query', new_columns)
# 获取日期字段的最大值
max_date_result = self.query("SELECT MAX(日期) as max_date FROM query")
if not max_date_result or not max_date_result[0]['max_date']:
self.logger.warning("源表中没有日期数据")
return {'status': '失败', 'message': '源表中没有日期数据', 'row_count': 0}
max_date = max_date_result[0]['max_date']
self.logger.info(f"获取到日期字段的最大值: {max_date}")
# 获取最大日期对应的行
query = f"SELECT * FROM query WHERE 日期 = '{max_date}'"
source_data = self.query(query)
if not source_data:
self.logger.warning(f"源表中没有日期为 {max_date} 的数据")
return {'status': '成功', 'message': f'源表中没有日期为 {max_date} 的数据', 'row_count': 0}
row_count = len(source_data)
self.logger.info(f"从源数据库获取到 {row_count} 条记录")
# 清空本地表
local_db.execute("DELETE FROM query")
self.logger.info("已清空本地query表")
# 插入数据到本地表
placeholders = ', '.join(['?' for _ in source_data[0].keys()])
columns = ', '.join(source_data[0].keys())
insert_sql = f"INSERT INTO query ({columns}) VALUES ({placeholders})"
for row in source_data:
local_db.execute(insert_sql, list(row.values()))
self.logger.info(f"成功同步 {row_count} 条记录到本地数据库")
return {
'status': '成功',
'message': f'成功同步 {row_count} 条记录',
'row_count': row_count
}
except Exception as e:
self.logger.error(f"同步query表失败: {str(e)}", exc_info=True)
return {
'status': '失败',
'message': f'同步失败: {str(e)}',
'row_count': 0
}
def sync_work_order_list_incremental(self, local_db: DatabaseManager) -> Dict[str, Any]:
"""
全表同步work_order_list表
使用分批处理方式,每批处理10000条记录,避免内存溢出
在同步前清空本地数据库中的表
Args:
local_db: 本地数据库管理器
Returns:
同步结果
"""
try:
self.logger.info("开始全表同步work_order_list表")
# 确保连接有效
if not self.conn:
self.connect()
# 获取源表结构
source_columns = self.get_table_columns('work_order_list')
if not source_columns:
self.logger.error("获取源表结构失败")
return {'status': '失败', 'message': '获取源表结构失败', 'row_count': 0}
# 检查本地表是否存在,如果不存在则创建
if not local_db.table_exists('work_order_list'):
self.logger.info("本地表不存在,创建表")
local_db.create_table('work_order_list', source_columns)
else:
# 检查是否需要添加新列
local_columns = local_db.get_table_info('work_order_list')
local_column_names = [col['name'] for col in local_columns]
new_columns = [col for col in source_columns if col['name'] not in local_column_names]
if new_columns:
self.logger.info(f"添加 {len(new_columns)} 个新列到本地表")
local_db.add_columns('work_order_list', new_columns)
# 清空本地表
local_db.execute("DELETE FROM work_order_list")
self.logger.info("已清空本地work_order_list表")
# 执行分批同步
return self._do_sync_work_order_list_full(local_db)
except Exception as e:
self.logger.error(f"同步work_order_list表失败: {str(e)}", exc_info=True)
return {
'status': '失败',
'message': f'同步失败: {str(e)}',
'row_count': 0
}
def _do_sync_work_order_list_full(self, local_db: DatabaseManager) -> Dict[str, Any]:
"""
执行work_order_list表的同步,只同步前一个月的数据
使用分批处理方式,每批处理10000条记录
Args:
local_db: 本地数据库管理器
Returns:
同步结果
"""
try:
# 计算一个月前的日期
one_month_ago = (datetime.datetime.now() - datetime.timedelta(days=30)).strftime('%Y-%m-%d')
# 获取一个月内的记录数
count_result = self.query(f"SELECT COUNT(*) as total FROM work_order_list WHERE createDate >= '{one_month_ago}'")
total_count = count_result[0]['total']
if total_count == 0:
self.logger.warning("源表中没有一个月内的数据")
return {'status': '成功', 'message': '源表中没有一个月内的数据', 'row_count': 0}
self.logger.info(f"源表中一个月内共有 {total_count} 条记录")
# 设置批处理大小
batch_size = 10000
# 计算总批次数
total_batches = (total_count + batch_size - 1) // batch_size
# 记录总同步行数
total_synced = 0
# 分批处理
for batch in range(total_batches):
offset = batch * batch_size
# 获取当前批次的数据,只获取一个月内的数据
query = f"SELECT * FROM work_order_list WHERE createDate >= '{one_month_ago}' LIMIT {batch_size} OFFSET {offset}"
batch_data = self.query(query)
if not batch_data:
self.logger.warning(f"批次 {batch + 1}/{total_batches} 没有数据")
continue
batch_count = len(batch_data)
self.logger.info(f"批次 {batch + 1}/{total_batches}: 获取到 {batch_count} 条记录")
# 插入数据到本地表
if batch_count > 0:
# 获取列名
columns = list(batch_data[0].keys())
placeholders = ', '.join(['?' for _ in columns])
columns_str = ', '.join(columns)
# 构建插入语句
insert_sql = f"INSERT INTO work_order_list ({columns_str}) VALUES ({placeholders})"
# 批量插入
for row in batch_data:
local_db.execute(insert_sql, list(row.values()))
total_synced += batch_count
self.logger.info(f"批次 {batch + 1}/{total_batches}: 已同步 {batch_count} 条记录,总计: {total_synced}/{total_count}")
self.logger.info(f"work_order_list表全表同步完成,共同步 {total_synced} 条记录")
return {
'status': '成功',
'message': f'全表同步完成,共同步 {total_synced} 条记录',
'row_count': total_synced
}
except Exception as e:
self.logger.error(f"执行work_order_list表全表同步失败: {str(e)}", exc_info=True)
return {
'status': '失败',
'message': f'全表同步失败: {str(e)}',
'row_count': 0
}
def sync_view_sop_progress(self, local_db: DatabaseManager) -> Dict[str, Any]:
"""
同步view_sop_progress表
一次性同步完成,每次写入前先清空表
Args:
local_db: 本地数据库管理器
Returns:
同步结果
"""
try:
self.logger.info("开始同步view_sop_progress表")
# 确保连接有效
if not self.conn:
self.connect()
# 获取源表结构
source_columns = self.get_table_columns('view_sop_progress')
if not source_columns:
self.logger.error("获取源表结构失败")
return {'status': '失败', 'message': '获取源表结构失败', 'row_count': 0}
# 检查本地表是否存在,如果不存在则创建
if not local_db.table_exists('view_sop_progress'):
self.logger.info("本地表不存在,创建表")
local_db.create_table('view_sop_progress', source_columns)
else:
# 检查是否需要添加新列
local_columns = local_db.get_table_info('view_sop_progress')
local_column_names = [col['name'] for col in local_columns]
new_columns = [col for col in source_columns if col['name'] not in local_column_names]
if new_columns:
self.logger.info(f"添加 {len(new_columns)} 个新列到本地表")
local_db.add_columns('view_sop_progress', new_columns)
# 获取源表数据
query = "SELECT * FROM view_sop_progress"
source_data = self.query(query)
if not source_data:
self.logger.warning("源表中没有数据")
return {'status': '成功', 'message': '源表中没有数据', 'row_count': 0}
row_count = len(source_data)
self.logger.info(f"从源数据库获取到 {row_count} 条记录")
# 清空本地表
local_db.execute("DELETE FROM view_sop_progress")
self.logger.info("已清空本地view_sop_progress表")
# 插入数据到本地表
if row_count > 0:
# 获取列名
columns = list(source_data[0].keys())
placeholders = ', '.join(['?' for _ in columns])
columns_str = ', '.join(columns)
# 构建插入语句
insert_sql = f"INSERT INTO view_sop_progress ({columns_str}) VALUES ({placeholders})"
# 批量插入
for row in source_data:
# 转换数据类型,处理 Decimal 类型
converted_values = []
for value in row.values():
if hasattr(value, '__class__') and value.__class__.__name__ == 'Decimal':
# 将 Decimal 转换为 float
converted_values.append(float(value))
else:
converted_values.append(value)
local_db.execute(insert_sql, converted_values)
self.logger.info(f"成功同步 {row_count} 条记录到本地数据库")
return {
'status': '成功',
'message': f'成功同步 {row_count} 条记录',
'row_count': row_count
}
except Exception as e:
self.logger.error(f"同步view_sop_progress表失败: {str(e)}", exc_info=True)
return {
'status': '失败',
'message': f'同步失败: {str(e)}',
'row_count': 0
}