05635b76b9
状态: - 9 个 sync task(stock_basic / kline_daily / kline_index / kline_5min / moneyflow / industry_sector / sector_features / share_snapshot / market_regime) - 数据源:baostock + mairui + 雪球(pysnowball) + 新浪(4 个) - 项目级约束:永远不用 akshare(已落实) - kline_5min 改用 DB 快照统一全量/增量逻辑 - 零后端 Chrome 扩展 xueqiu_sync(独立项目)
133 lines
5.4 KiB
Python
133 lines
5.4 KiB
Python
"""CLI 入口:手动触发 / 状态查询。"""
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import json
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
_PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||
if str(_PROJECT_ROOT) not in sys.path:
|
||
sys.path.insert(0, str(_PROJECT_ROOT))
|
||
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(
|
||
description="market_data_sync CLI",
|
||
)
|
||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||
|
||
# sync <task_id>
|
||
p_sync = sub.add_parser("sync", help="手动执行一次同步任务")
|
||
p_sync.add_argument("task_id", help="dataset_id, 例如 kline_daily")
|
||
p_sync.add_argument("--codes", help="指定股票代码,逗号分隔", default=None)
|
||
p_sync.add_argument("--start", help="起始日期 YYYY-MM-DD", default=None)
|
||
p_sync.add_argument("--end", help="结束日期 YYYY-MM-DD", default=None)
|
||
p_sync.add_argument("--workers", type=int, default=10, help="并发数")
|
||
|
||
sub.add_parser("list", help="列出所有同步任务")
|
||
sub.add_parser("status", help="查看整体状态(调度器 / 数据源 / 同步任务)")
|
||
sub.add_parser("datasources", help="查看数据源健康度")
|
||
sub.add_parser("schedule", help="查看计划任务")
|
||
sub.add_parser("reseed", help="重新 seed 默认计划任务到 config 表")
|
||
|
||
args = parser.parse_args()
|
||
|
||
if args.cmd == "sync":
|
||
from app.core.utils.logging import setup_logging
|
||
setup_logging()
|
||
from app.tasks import get_task
|
||
from app.core.datasource.registry import build_default_registry
|
||
from app.core.sync.registry import seed_sync_registry
|
||
build_default_registry()
|
||
seed_sync_registry() # 确保 dataset_registry 有这条任务定义
|
||
try:
|
||
task = get_task(args.task_id)
|
||
except KeyError as e:
|
||
print(f"错误: {e}")
|
||
sys.exit(1)
|
||
kwargs = {}
|
||
if args.codes:
|
||
kwargs["codes"] = [c.strip() for c in args.codes.split(",") if c.strip()]
|
||
if args.start:
|
||
kwargs["start"] = args.start
|
||
if args.end:
|
||
kwargs["end"] = args.end
|
||
if args.workers:
|
||
kwargs["max_workers"] = args.workers
|
||
result = task.run(trigger_source="cli", **kwargs)
|
||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||
sys.exit(0 if result.get("status") in ("ok", "warning") else 2)
|
||
|
||
if args.cmd == "list":
|
||
from app.tasks import TASKS
|
||
print("可用同步任务:")
|
||
for tid, cls in TASKS.items():
|
||
doc = (cls.__doc__ or cls.__name__).split("\n")[0]
|
||
print(f" {tid:25s} {doc}")
|
||
return
|
||
|
||
if args.cmd == "status":
|
||
from app.core.utils.logging import setup_logging
|
||
setup_logging()
|
||
from app.core.scheduler import get_scheduler_status
|
||
from app.core.datasource.registry import build_default_registry, get_health_status
|
||
from app.core.sync import get_registry_status
|
||
build_default_registry()
|
||
print("\n=== 调度器 ===")
|
||
s = get_scheduler_status()
|
||
print(json.dumps(s, ensure_ascii=False, indent=2, default=str))
|
||
print("\n=== 数据源健康度 ===")
|
||
for r in get_health_status():
|
||
mark = "✓" if r.get("success") else "✗"
|
||
print(f" {mark} {r.get('name', r.get('key')):30s} {r.get('message', '')}")
|
||
print("\n=== 同步任务状态 ===")
|
||
for r in get_registry_status():
|
||
print(f" {r['dataset_id']:25s} status={r.get('status'):10s} "
|
||
f"lastRun={r.get('last_success_at') or r.get('last_failure_at') or '-':20s} "
|
||
f"msg={r.get('message', '')[:60]}")
|
||
return
|
||
|
||
if args.cmd == "datasources":
|
||
from app.core.utils.logging import setup_logging
|
||
setup_logging()
|
||
from app.core.datasource.registry import build_default_registry, get_health_status, run_health_check
|
||
build_default_registry()
|
||
print("正在跑连通性测试...")
|
||
run_health_check()
|
||
for r in get_health_status():
|
||
mark = "✓" if r.get("success") else "✗"
|
||
print(f" {mark} {r.get('name', r.get('key')):30s} provides={r.get('provides', [])} {r.get('message', '')}")
|
||
return
|
||
|
||
if args.cmd == "schedule":
|
||
from app.core.utils.logging import setup_logging
|
||
setup_logging()
|
||
from app.core.scheduler import get_scheduler_status, seed_schedule_configs
|
||
seed_schedule_configs()
|
||
s = get_scheduler_status()
|
||
print(f"调度器运行中: {s['running']}")
|
||
for j in s["jobs"]:
|
||
enabled = "✓" if j.get("enabled") else "✗"
|
||
trigger = j.get("time") or f"every {j.get('intervalSeconds')}s"
|
||
print(f" {enabled} {j.get('name', j['key']):30s} {trigger:15s} "
|
||
f"condition={j.get('condition', 'always'):12s} "
|
||
f"last={j.get('lastStatus', '-')}")
|
||
return
|
||
|
||
if args.cmd == "reseed":
|
||
from app.core.utils.logging import setup_logging
|
||
setup_logging()
|
||
from app.core.scheduler.scheduler import seed_schedule_configs
|
||
from app.core.sync.registry import seed_sync_registry
|
||
from app.core.datasource.registry import seed_datasource_configs
|
||
seed_datasource_configs()
|
||
seed_sync_registry()
|
||
seed_schedule_configs()
|
||
print("已重新 seed:datasources + sync_registry + schedules")
|
||
return
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|