|
|
"""管理服务 - 对应Go的internal/service/admin.go"""
|
|
|
from datetime import datetime
|
|
|
from typing import Optional
|
|
|
import uuid
|
|
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
from sqlalchemy import text
|
|
|
|
|
|
from app.models import (
|
|
|
DataSourceStatusData, DataSourceInfo, SourceSwitchRequest,
|
|
|
BackfillRequest, HealthResponse, DataSourceStatus
|
|
|
)
|
|
|
from app.core.config import get_config, save_config
|
|
|
from app.core.logger import info
|
|
|
|
|
|
|
|
|
class AdminService:
|
|
|
"""管理服务"""
|
|
|
|
|
|
def __init__(self, db: Session):
|
|
|
self.db = db
|
|
|
|
|
|
def get_data_source_status(self) -> DataSourceStatusData:
|
|
|
"""获取数据源状态"""
|
|
|
config = get_config()
|
|
|
|
|
|
try:
|
|
|
# 查询数据库中的数据源配置
|
|
|
result = self.db.execute(text("""
|
|
|
SELECT asset_class, active_source, standby_sources, updated_at
|
|
|
FROM data_source_config
|
|
|
"""))
|
|
|
|
|
|
data = DataSourceStatusData()
|
|
|
has_data = False
|
|
|
|
|
|
for row in result:
|
|
|
has_data = True
|
|
|
asset_class, active_source, standby_sources, updated_at = row
|
|
|
|
|
|
info_obj = DataSourceInfo(
|
|
|
active_source=active_source,
|
|
|
standby_sources=standby_sources or [],
|
|
|
status=DataSourceStatus.HEALTHY
|
|
|
)
|
|
|
|
|
|
if asset_class == "stock":
|
|
|
data.stock = info_obj
|
|
|
elif asset_class == "futures":
|
|
|
data.futures = info_obj
|
|
|
|
|
|
# 如果没有数据库配置,使用配置文件中的设置
|
|
|
if not has_data:
|
|
|
data.stock.active_source = config.sources.stock.active
|
|
|
data.futures.active_source = config.sources.futures.active
|
|
|
|
|
|
return data
|
|
|
except Exception as e:
|
|
|
info(f"Data source config not found, using config file: {e}")
|
|
|
# 使用配置文件中的设置
|
|
|
data = DataSourceStatusData()
|
|
|
data.stock.active_source = config.sources.stock.active
|
|
|
data.futures.active_source = config.sources.futures.active
|
|
|
return data
|
|
|
|
|
|
def switch_data_source(self, req: SourceSwitchRequest) -> None:
|
|
|
"""切换数据源"""
|
|
|
from app.core.config import get_config, save_config
|
|
|
|
|
|
config = get_config()
|
|
|
|
|
|
# 更新内存中的配置
|
|
|
if req.asset_class.value == "all":
|
|
|
config.sources.stock.active = req.source
|
|
|
config.sources.futures.active = req.source
|
|
|
elif req.asset_class.value == "stock":
|
|
|
config.sources.stock.active = req.source
|
|
|
elif req.asset_class.value == "futures":
|
|
|
config.sources.futures.active = req.source
|
|
|
|
|
|
# 保存到配置文件
|
|
|
try:
|
|
|
save_config(config)
|
|
|
except Exception as e:
|
|
|
info(f"Failed to save config: {e}")
|
|
|
|
|
|
# 同时尝试保存到数据库(如果支持)
|
|
|
try:
|
|
|
asset_classes = []
|
|
|
if req.asset_class.value == "all":
|
|
|
asset_classes = ["stock", "futures"]
|
|
|
else:
|
|
|
asset_classes = [req.asset_class.value]
|
|
|
|
|
|
for ac in asset_classes:
|
|
|
# MySQL: INSERT ... ON DUPLICATE KEY UPDATE
|
|
|
self.db.execute(
|
|
|
text("""
|
|
|
INSERT INTO data_source_config
|
|
|
(asset_class, active_source, updated_at)
|
|
|
VALUES (:asset_class, :source, NOW())
|
|
|
ON DUPLICATE KEY UPDATE
|
|
|
active_source = VALUES(active_source),
|
|
|
updated_at = VALUES(updated_at)
|
|
|
"""),
|
|
|
{"asset_class": ac, "source": req.source}
|
|
|
)
|
|
|
|
|
|
self.db.commit()
|
|
|
except Exception as e:
|
|
|
info(f"Database update failed (using config file only): {e}")
|
|
|
self.db.rollback()
|
|
|
|
|
|
# 如果需要同步补录,启动后台任务
|
|
|
if req.sync_backfill:
|
|
|
info(f"Starting backfill for {req.asset_class} from {req.start_date}")
|
|
|
# TODO: 启动异步补录任务
|
|
|
|
|
|
def backfill_data(self, req: BackfillRequest) -> str:
|
|
|
"""历史数据补录"""
|
|
|
task_id = str(uuid.uuid4())
|
|
|
|
|
|
info(f"Starting backfill task {task_id} for {req.asset_class}")
|
|
|
# TODO: 将补录任务存入数据库,启动后台Worker执行
|
|
|
|
|
|
return task_id
|
|
|
|
|
|
def health_check(self) -> HealthResponse:
|
|
|
"""健康检查"""
|
|
|
try:
|
|
|
# 检查数据库连接
|
|
|
self.db.execute(text("SELECT 1"))
|
|
|
|
|
|
return HealthResponse(
|
|
|
status="healthy",
|
|
|
timestamp=datetime.now()
|
|
|
)
|
|
|
except Exception as e:
|
|
|
return HealthResponse(
|
|
|
status=f"unhealthy: {str(e)}",
|
|
|
timestamp=datetime.now()
|
|
|
)
|