Небольшие изменения

This commit is contained in:
brusnitsyn
2026-03-13 17:11:39 +09:00
parent c201d36ae6
commit de2dd82fa1
18 changed files with 1140 additions and 491 deletions

View File

@@ -2,7 +2,9 @@
from typing import Optional, List
from datetime import datetime
from sqlalchemy.orm import Session
from fastapi import HTTPException
from sqlalchemy import select
from sqlalchemy.orm import Session, selectinload
from app.core.database import db_connector
from app.models.replication import ReplicationMetadata, ReplicationLog, ReplicationSchedule
from app.core.logging import migration_logger
@@ -21,25 +23,25 @@ class ReplicationMetadataRepo:
Base.metadata.create_all(self.engine)
migration_logger.info("Таблица replication_metadata создана/проверена")
def get_table_metadata(self, table_name: str) -> Optional[ReplicationMetadata]:
def get_table_metadata(self, metadata_id: int) -> Optional[ReplicationMetadata]:
"""Получает метаданные для таблицы"""
session = self.SessionLocal()
try:
metadata = session.query(ReplicationMetadata).filter_by(
table_name=table_name
id=metadata_id
).first()
# Если нет, создаем
if not metadata:
metadata = ReplicationMetadata.create_if_not_exists(session, table_name)
# if not metadata:
# metadata = ReplicationMetadata.create_if_not_exists(session, table_name)
return metadata
finally:
session.close()
def get_last_sync_time(self, table_name: str) -> Optional[datetime]:
def get_last_sync_time(self, metadata_id: int) -> Optional[datetime]:
"""Получает время последней синхронизации таблицы"""
metadata = self.get_table_metadata(table_name)
metadata = self.get_table_metadata(metadata_id)
return metadata.last_sync_time if metadata else None
def get_last_id(self, table_name: str) -> Optional[int]:
@@ -47,31 +49,29 @@ class ReplicationMetadataRepo:
metadata = self.get_table_metadata(table_name)
return metadata.last_id if metadata else None
def update_sync_time(self, table_name: str) -> bool:
def update_sync_time(self, schedule_id: int) -> bool:
"""Обновляет время синхронизации таблицы"""
session = self.SessionLocal()
try:
metadata = session.query(ReplicationMetadata).filter_by(
table_name=table_name
schedule = session.query(ReplicationSchedule).filter_by(
id=schedule_id
).first()
if not metadata:
metadata = ReplicationMetadata(
table_name=table_name,
last_sync_time=datetime.now(),
last_id=0
)
session.add(metadata)
if not schedule:
return False
else:
metadata.last_sync_time = datetime.now()
metadata.updated_at = datetime.now()
datenow = datetime.now()
schedule.last_run = datenow
metadata = schedule.table
metadata.last_sync_time = datenow
metadata.updated_at = datenow
session.commit()
migration_logger.debug(f"Updated sync time for {table_name} to {metadata.last_sync_time}")
migration_logger.debug(f"Updated sync time for {metadata.table_name} to {metadata.last_sync_time}")
return True
except Exception as e:
session.rollback()
migration_logger.error(f"Error updating sync time for {table_name}: {e}")
migration_logger.error(f"Ошибка при обновлении времени синхронизации распиания с ID: {schedule_id}: {e}")
return False
finally:
session.close()
@@ -80,20 +80,16 @@ class ReplicationMetadataRepo:
"""Обновляет последний обработанный ID"""
session = self.SessionLocal()
try:
metadata = session.query(ReplicationMetadata).filter_by(
metadatas = session.query(ReplicationMetadata).filter_by(
table_name=table_name
).first()
).all()
if not metadata:
metadata = ReplicationMetadata(
table_name=table_name,
last_sync_time=datetime.now(),
last_id=last_id
)
session.add(metadata)
if not metadatas:
return False
else:
metadata.last_id = last_id
metadata.updated_at = datetime.now()
for metadata in metadatas:
metadata.last_id = last_id
metadata.updated_at = datetime.now()
session.commit()
migration_logger.debug(f"Updated last_id for {table_name} to {last_id}")
@@ -186,37 +182,74 @@ class ReplicationMetadataRepo:
# ========== Методы для расписаний ==========
def init_default_schedules(self, table_names: List[str]):
def init_default_schedules(self, metadatas: List[ReplicationMetadata]):
"""Инициализирует расписания по умолчанию для списка таблиц"""
session = self.get_session()
try:
for table_name in table_names:
init_count = 0
for metadata in metadatas:
# Проверяем, есть ли уже расписание
schedule = session.query(ReplicationSchedule).filter_by(
table_name=table_name
).first()
query = session.query(ReplicationSchedule).filter_by(
metadata_id=metadata.id
)
schedule_exist = session.query(query.exists())
if not schedule:
if not schedule_exist:
job_name = f"{metadata.table_name} - расписание по умолчанию"
# Создаем расписание по умолчанию (каждый день в 00:00)
schedule = ReplicationSchedule(
table_name=table_name,
metadata_id=metadata.id,
name=job_name,
schedule_time=datetime.strptime("00:00", "%H:%M").time(),
days=[], # Пустой список = все дни
full_reload=False,
enabled=True
enabled=False
)
session.add(schedule)
migration_logger.debug(f"Создано расписание по умолчанию для {table_name}")
migration_logger.debug(f"Создано расписание по умолчанию для {metadata.table_name}")
init_count += 1
session.commit()
migration_logger.info(f"Инициализированы расписания по умолчанию для {len(table_names)} таблиц")
migration_logger.info(f"Инициализированы расписания по умолчанию для {init_count} таблиц")
except Exception as e:
session.rollback()
migration_logger.error(f"Ошибка инициализации расписаний: {e}")
finally:
session.close()
def add_metadata(self, table_name: str, life_table_name: Optional[str] = None,
description: Optional[str] = None, enabled: Optional[bool] = False) -> Optional[ReplicationMetadata]:
"""Добавить метадату"""
session = self.get_session()
try:
metadata_exists = session.query(ReplicationMetadata).filter_by(
table_name=table_name
).first()
if metadata_exists:
raise HTTPException(status_code=400, detail=f"Метадата с таблицей {metadata_exists.table_name} существует")
metadata = ReplicationMetadata(
table_name=table_name,
life_table_name=life_table_name,
description=description,
is_active=enabled
)
session.add(metadata)
session.commit()
migration_logger.info(f"Добавлена новая метадата для {metadata.table_name}")
return metadata
except Exception as e:
session.rollback()
migration_logger.error(f"Ошибка добавления расписания: {e}")
return None
finally:
session.close()
def add_schedule(self, table_name: str, schedule_time: str, days: Optional[List[str]] = None,
def add_schedule(self, metadata_id: int, schedule_time: str, days: Optional[List[str]] = None,
full_reload: bool = False, enabled: bool = True,
name: Optional[str] = None, description: Optional[str] = None) -> Optional[ReplicationSchedule]:
"""Добавить НОВОЕ расписание для таблицы"""
@@ -224,33 +257,33 @@ class ReplicationMetadataRepo:
try:
# Проверяем, существует ли метаданные
metadata = session.query(ReplicationMetadata).filter_by(
table_name=table_name
id=metadata_id
).first()
if not metadata:
metadata = self._create_metadata(session, table_name)
raise HTTPException(status_code=404, detail=f"Метадата с ID {metadata_id} не найдена")
# Парсим время
time_obj = datetime.strptime(schedule_time, "%H:%M").time()
# Создаем новое расписание
schedule = ReplicationSchedule(
table_name=table_name,
metadata_id=metadata_id,
schedule_time=time_obj,
days=days if days else [],
full_reload=full_reload,
enabled=enabled,
name=name or f"{schedule_time} - {'полная' if full_reload else 'инкремент'}",
name=name or f"{metadata.table_name} - {'полная' if full_reload else 'инкремент'}",
description=description
)
session.add(schedule)
session.commit()
migration_logger.info(f"Добавлено новое расписание для {table_name} в {schedule_time}")
migration_logger.info(f"Добавлено новое расписание для {metadata.table_name} в {schedule_time}")
return schedule.to_dict()
except Exception as e:
session.rollback()
migration_logger.error(f"Ошибка добавления расписания для {table_name}: {e}")
migration_logger.error(f"Ошибка добавления расписания: {e}")
return None
finally:
session.close()
@@ -266,7 +299,7 @@ class ReplicationMetadataRepo:
if schedule:
session.delete(schedule)
session.commit()
migration_logger.info(f"Удалено расписание ID={schedule_id} для {schedule.table_name}")
migration_logger.info(f"Удалено расписание ID={schedule_id} для {schedule.table.table_name}")
return True
return False
except Exception as e:
@@ -303,12 +336,12 @@ class ReplicationMetadataRepo:
finally:
session.close()
def get_schedule(self, table_name: str) -> List[ReplicationSchedule]:
def get_schedule(self, metadata_id: int) -> List[ReplicationSchedule]:
"""Получает расписание для таблицы"""
session = self.get_session()
try:
schedule = session.query(ReplicationSchedule).filter_by(
table_name=table_name
metadata_id=metadata_id
).all()
return schedule
finally:
@@ -318,7 +351,24 @@ class ReplicationMetadataRepo:
"""Получает все расписания"""
session = self.get_session()
try:
return session.query(ReplicationSchedule).all()
schedules = session.execute(
select(ReplicationSchedule)
.options(selectinload(ReplicationSchedule.table))
).scalars().all()
# Явно пометить сессию как не expiring (чтобы объекты не expire после commit)
for schedule in schedules:
session.expunge(schedule) # Удалить из сессии, но сохранить данные
return schedules
finally:
session.close()
def get_all_metadata(self) -> List[ReplicationMetadata]:
"""Получает все метадаты"""
session = self.get_session()
try:
return session.query(ReplicationMetadata).all()
finally:
session.close()
@@ -351,12 +401,12 @@ class ReplicationMetadataRepo:
finally:
session.close()
def update_schedule_last_run(self, table_name: str) -> bool:
def update_schedule_last_run(self, schedule_id: int) -> bool:
"""Обновляет время последнего запуска расписания"""
session = self.get_session()
try:
schedule = session.query(ReplicationSchedule).filter_by(
table_name=table_name
id=schedule_id
).first()
if schedule:
@@ -367,51 +417,51 @@ class ReplicationMetadataRepo:
return False
except Exception as e:
session.rollback()
migration_logger.error(f"Ошибка обновления last_run для {table_name}: {e}")
migration_logger.error(f"Ошибка обновления last_run: {e}")
return False
finally:
session.close()
def disable_schedule(self, table_name: str) -> bool:
def disable_schedule(self, schedule_id: int) -> bool:
"""Отключает расписание"""
session = self.get_session()
try:
schedule = session.query(ReplicationSchedule).filter_by(
table_name=table_name
id=schedule_id
).first()
if schedule:
schedule.enabled = False
schedule.updated_at = datetime.now()
session.commit()
migration_logger.info(f"Отключено расписание для {table_name}")
migration_logger.info(f"Отключено расписание ID={schedule_id} для {schedule.table.table_name}")
return True
return False
except Exception as e:
session.rollback()
migration_logger.error(f"Ошибка отключения расписания для {table_name}: {e}")
migration_logger.error(f"Ошибка отключения расписания: {e}")
return False
finally:
session.close()
def enable_schedule(self, table_name: str) -> bool:
def enable_schedule(self, schedule_id: int) -> bool:
"""Включает расписание"""
session = self.get_session()
try:
schedule = session.query(ReplicationSchedule).filter_by(
table_name=table_name
id=schedule_id
).first()
if schedule:
schedule.enabled = True
schedule.updated_at = datetime.now()
session.commit()
migration_logger.info(f"Включено расписание для {table_name}")
migration_logger.info(f"Включено расписание ID={schedule_id} для {schedule.table.table_name}")
return True
return False
except Exception as e:
session.rollback()
migration_logger.error(f"Ошибка включения расписания для {table_name}: {e}")
migration_logger.error(f"Ошибка включения расписания: {e}")
return False
finally:
session.close()
@@ -428,15 +478,16 @@ class ReplicationMetadataRepo:
session = self.get_session()
try:
metadata = session.query(ReplicationMetadata).filter_by(
metadatas = session.query(ReplicationMetadata).filter_by(
table_name=table_name
).first()
if metadata:
metadata.total_rows = stats['total_rows']
metadata.last_id = stats['max_id']
metadata.updated_at = datetime.now()
session.commit()
).all()
if metadatas:
for metadata in metadatas:
metadata.total_rows = stats['total_rows']
metadata.last_id = stats['max_id']
metadata.updated_at = datetime.now()
session.commit()
migration_logger.info(f"Синхронизирована статистика {table_name}: {stats['total_rows']} строк, max_id={stats['max_id']}")
else: