from __future__ import annotations import argparse import json import logging import os import time from typing import Any from inventory_analytics.logging_utils import exception_fields from inventory_analytics.logging_utils import log_event as emit_log_event from inventory_analytics.logging_utils import sanitize_value 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") LOG_FIELD_LABELS = { "action": "动作", "elapsed_ms": "耗时毫秒", "error": "错误", "error_type": "异常类型", "fallback": "兜底方式", "loaded": "已加载", "row_count": "行数", "source_row_count": "源数据行数", "stage": "阶段", "table_count": "表数量", "target_count": "目标数量", } def log_event(level: int, event: str, **fields: Any) -> None: emit_log_event(logger, LOG_FIELD_LABELS, level, event, **fields) 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 safe_result_summary(result: dict[str, object]) -> dict[str, object]: summary: dict[str, object] = {} feishu_result = result.get("feishu") if isinstance(feishu_result, dict): summary["feishu"] = {"table_count": feishu_result.get("table_count")} postgres_result = result.get("postgres") if isinstance(postgres_result, dict): summary["postgres"] = {"row_count": postgres_result.get("row_count")} sync_result = result.get("sync_brand") if isinstance(sync_result, dict): summary["sync_brand"] = { "target_count": sync_result.get("target_count"), "source_row_count": sync_result.get("source_row_count"), } return summary def public_result(result: dict[str, object]) -> dict[str, object]: return sanitize_value(result) def main() -> int: started_at = time.monotonic() try: args = parse_args() safe_logs = os.getenv("INVENTORY_SAFE_LOGS") == "1" log_event(logging.INFO, "渠道库存同步任务启动", action=args.action, stage="start") config = build_app_config() if config.env_path: log_event(logging.INFO, "配置加载完成", loaded=True, stage="config") else: log_event(logging.WARNING, "未找到环境变量文件,使用当前Shell环境变量", fallback="shell", stage="config") client = FeishuClient(config) result: dict[str, object] = {} if args.action in {"feishu", "all"}: log_event(logging.INFO, "开始读取飞书表信息", stage="feishu") result["feishu"] = run_feishu_summary(client) log_event( logging.INFO, "飞书表信息读取完成", stage="feishu", table_count=result["feishu"].get("table_count") if isinstance(result["feishu"], dict) else None, ) if args.action in {"postgres", "all"}: log_event(logging.INFO, "开始查询渠道库存数据", stage="database") result["postgres"] = query_postgres(config) log_event( logging.INFO, "渠道库存数据查询完成", stage="database", 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, "开始同步渠道库存到飞书", stage="sync") 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, "渠道库存同步到飞书完成", stage="sync", 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, ) if safe_logs: print(json.dumps(safe_result_summary(result), ensure_ascii=False, indent=2)) else: print(json.dumps(public_result(result), ensure_ascii=False, indent=2)) log_event( logging.INFO, "渠道库存同步任务完成", action=args.action, stage="finish", elapsed_ms=int((time.monotonic() - started_at) * 1000), ) return 0 except Exception as exc: # noqa: BLE001 log_event( logging.ERROR, "渠道库存同步任务失败", stage="failed", **exception_fields(exc), elapsed_ms=int((time.monotonic() - started_at) * 1000), ) return 1