在准备环境前提交次全部更改。
This commit is contained in:
152
scripts/ops/analyze_dataflow.py
Normal file
152
scripts/ops/analyze_dataflow.py
Normal file
@@ -0,0 +1,152 @@
|
||||
"""
|
||||
数据流结构分析 — CLI 入口
|
||||
|
||||
用法:
|
||||
python scripts/ops/analyze_dataflow.py
|
||||
python scripts/ops/analyze_dataflow.py --date-from 2025-01-01 --date-to 2025-01-15
|
||||
python scripts/ops/analyze_dataflow.py --limit 100 --tables settlement_records,payment_transactions
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
"""
|
||||
构造 CLI 参数解析器。
|
||||
|
||||
参数:
|
||||
--date-from 数据获取起始日期 (YYYY-MM-DD)
|
||||
--date-to 数据获取截止日期 (YYYY-MM-DD)
|
||||
--limit 每端点最大记录数 (默认 200)
|
||||
--tables 要分析的表名列表 (逗号分隔,缺省=全部)
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="数据流结构分析 — 采集 API JSON 和 DB 表结构",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--date-from",
|
||||
type=str,
|
||||
default=None,
|
||||
help="数据获取起始日期 (YYYY-MM-DD)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--date-to",
|
||||
type=str,
|
||||
default=None,
|
||||
help="数据获取截止日期 (YYYY-MM-DD)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--limit",
|
||||
type=int,
|
||||
default=200,
|
||||
help="每端点最大记录数 (默认 200)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--tables",
|
||||
type=str,
|
||||
default=None,
|
||||
help="要分析的表名列表 (逗号分隔,缺省=全部)",
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
def resolve_output_dir() -> Path:
|
||||
"""
|
||||
确定输出目录:
|
||||
1. 优先读取环境变量 SYSTEM_ANALYZE_ROOT
|
||||
2. 回退到 docs/reports/
|
||||
3. 确保目录存在(自动创建)
|
||||
"""
|
||||
env_root = os.environ.get("SYSTEM_ANALYZE_ROOT")
|
||||
if env_root:
|
||||
out = Path(env_root)
|
||||
else:
|
||||
out = Path("docs/reports")
|
||||
out.mkdir(parents=True, exist_ok=True)
|
||||
return out
|
||||
|
||||
|
||||
def generate_output_filename(dt: "datetime") -> str:
|
||||
"""生成输出文件名:dataflow_YYYY-MM-DD_HHMMSS.md"""
|
||||
return f"dataflow_{dt.strftime('%Y-%m-%d_%H%M%S')}.md"
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""
|
||||
串联采集流程:
|
||||
1. 解析 CLI 参数
|
||||
2. 加载环境变量(.env 分层叠加)
|
||||
3. 构造 AnalyzerConfig
|
||||
4. 调用 collect_all_tables() 执行采集
|
||||
5. 调用 dump_collection_results() 落盘
|
||||
6. 输出采集摘要到 stdout
|
||||
"""
|
||||
from datetime import date as _date, datetime as _datetime
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# ── 1. 解析 CLI 参数 ──
|
||||
parser = build_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
# ── 2. 加载环境变量(分层叠加:根 .env < ETL .env < 环境变量) ──
|
||||
# override=False 保证后加载的不覆盖先加载的环境变量
|
||||
# 先加载根 .env(最低优先级)
|
||||
load_dotenv(Path(".env"), override=False)
|
||||
# 再加载 ETL 专属 .env(中优先级)
|
||||
load_dotenv(Path("apps/etl/connectors/feiqiu/.env"), override=False)
|
||||
# 真实环境变量(最高优先级)已自动存在于 os.environ
|
||||
|
||||
# ── 3. 构造 AnalyzerConfig ──
|
||||
date_from = _date.fromisoformat(args.date_from) if args.date_from else None
|
||||
date_to = _date.fromisoformat(args.date_to) if args.date_to else None
|
||||
tables = [t.strip() for t in args.tables.split(",")] if args.tables else None
|
||||
output_dir = resolve_output_dir()
|
||||
|
||||
from dataflow_analyzer import AnalyzerConfig, ODS_SPECS, collect_all_tables, dump_collection_results
|
||||
|
||||
config = AnalyzerConfig(
|
||||
date_from=date_from,
|
||||
date_to=date_to,
|
||||
limit=args.limit,
|
||||
tables=tables,
|
||||
output_dir=output_dir,
|
||||
pg_dsn=os.environ.get("DATABASE_URL") or os.environ.get("PG_DSN", ""),
|
||||
api_base=os.environ.get("API_BASE", ""),
|
||||
api_token=os.environ.get("API_TOKEN", ""),
|
||||
store_id=os.environ.get("STORE_ID", ""),
|
||||
)
|
||||
|
||||
# ── 4. 执行采集(使用本模块的 ODS_SPECS) ──
|
||||
results = collect_all_tables(config, specs=ODS_SPECS)
|
||||
|
||||
# ── 5. 落盘 ──
|
||||
paths = dump_collection_results(results, output_dir)
|
||||
|
||||
# ── 6. 输出采集摘要 ──
|
||||
now = _datetime.now()
|
||||
filename = generate_output_filename(now)
|
||||
ok = sum(1 for r in results if r.error is None)
|
||||
fail = len(results) - ok
|
||||
total_records = sum(r.record_count for r in results)
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"数据流结构分析完成")
|
||||
print(f"{'='*60}")
|
||||
print(f" 输出目录: {output_dir}")
|
||||
print(f" 报告文件名: {filename}")
|
||||
print(f" 分析表数: {len(results)} ({ok} 成功, {fail} 失败)")
|
||||
print(f" 总记录数: {total_records}")
|
||||
print(f" 落盘路径:")
|
||||
for category, p in paths.items():
|
||||
print(f" {category}: {p}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user