from __future__ import annotations import argparse import json import logging import re import time from typing import Any from .feishu import FeishuClient from .postgres import query_postgres from .service import run_feishu_summary, run_sync_brand from .settings import build_app_config logger = logging.getLogger("inventory_sync.cli") SENSITIVE_PATTERN = re.compile(r"(?i)(password|passwd|secret|token|authorization)\s*[:=]\s*([^\s,;]+)") LOG_FIELD_LABELS = { "action": "动作", "elapsed_ms": "耗时毫秒", "env_path": "环境文件", "error": "错误", "fallback": "兜底方式", "row_count": "行数", "source_row_count": "源数据行数", "table_count": "表数量", "target_count": "目标数量", } def sanitize_value(value: Any) -> Any: if isinstance(value, str): return SENSITIVE_PATTERN.sub(r"\1=***", value) return value def log_event(level: int, event: str, **fields: Any) -> None: safe_event = sanitize_value(event) details = " ".join( f"{LOG_FIELD_LABELS.get(key, key)}={sanitize_value(value)!r}" for key, value in fields.items() ) logger.log(level, "事件=%s%s", safe_event, f" {details}" if details else "") def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description="Feishu Base and PostgreSQL helper") parser.add_argument( "--action", choices=["feishu", "postgres", "sync-brand", "all"], default="feishu", help="要执行的动作,默认 feishu", ) return parser.parse_args() def main() -> int: started_at = time.monotonic() try: args = parse_args() log_event(logging.INFO, "飞书同步动作开始", action=args.action) config = build_app_config() if config.env_path: log_event(logging.INFO, "已加载环境变量文件", env_path=config.env_path) else: log_event(logging.WARNING, "未找到环境变量文件", fallback="当前Shell环境变量") client = FeishuClient(config) result: dict[str, object] = {} if args.action in {"feishu", "all"}: log_event(logging.INFO, "开始读取飞书Base信息") result["feishu"] = run_feishu_summary(client) log_event( logging.INFO, "飞书Base信息读取完成", table_count=result["feishu"].get("table_count") if isinstance(result["feishu"], dict) else None, ) if args.action in {"postgres", "all"}: log_event(logging.INFO, "开始查询PostgreSQL") result["postgres"] = query_postgres(config) log_event( logging.INFO, "PostgreSQL查询完成", row_count=result["postgres"].get("row_count") if isinstance(result["postgres"], dict) else None, ) if args.action in {"sync-brand", "all"}: log_event(logging.INFO, "开始同步品牌数据") postgres_result = result.get("postgres") result["sync_brand"] = run_sync_brand(config, client, postgres_result if isinstance(postgres_result, dict) else None) log_event( logging.INFO, "品牌数据同步完成", target_count=result["sync_brand"].get("target_count") if isinstance(result["sync_brand"], dict) else None, source_row_count=result["sync_brand"].get("source_row_count") if isinstance(result["sync_brand"], dict) else None, ) print(json.dumps(result, ensure_ascii=False, indent=2)) log_event( logging.INFO, "飞书同步动作完成", action=args.action, elapsed_ms=int((time.monotonic() - started_at) * 1000), ) return 0 except Exception as exc: # noqa: BLE001 log_event( logging.ERROR, "飞书同步动作失败", error=exc, elapsed_ms=int((time.monotonic() - started_at) * 1000), ) return 1