"""DEBUG 联调发现的问题。一次性脚本。""" import psycopg2, psycopg2.extras, os, json from dotenv import load_dotenv from pathlib import Path load_dotenv(Path(__file__).resolve().parents[2] / ".env") dsn = os.environ["PG_DSN"] conn = psycopg2.connect(dsn) cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) print("=" * 60) print("1. dwd.dwd_settlement_head 中 member 相关列") print("=" * 60) cur.execute(""" SELECT column_name, data_type FROM information_schema.columns WHERE table_schema='dwd' AND table_name='dwd_settlement_head' AND column_name LIKE '%%member%%' ORDER BY ordinal_position """) for r in cur.fetchall(): print(f" {r['column_name']} ({r['data_type']})") print() print("=" * 60) print("2. dws_member_consumption_summary 金额负值记录") print("=" * 60) # 先查表结构 cur.execute(""" SELECT column_name FROM information_schema.columns WHERE table_schema='dws' AND table_name='dws_member_consumption_summary' AND column_name LIKE '%%consume%%' OR (table_schema='dws' AND table_name='dws_member_consumption_summary' AND column_name LIKE '%%amount%%') ORDER BY ordinal_position """) print(" consume/amount 相关列:") for r in cur.fetchall(): print(f" {r['column_name']}") conn.rollback() cur.execute(""" SELECT member_id, total_consume_amount, site_id FROM dws.dws_member_consumption_summary WHERE total_consume_amount < 0 ORDER BY total_consume_amount LIMIT 3 """) neg_rows = cur.fetchall() for r in neg_rows: print(f" member_id={r['member_id']}, amount={r['total_consume_amount']}, site={r['site_id']}") neg_member = neg_rows[0]['member_id'] if neg_rows else None neg_site = neg_rows[0]['site_id'] if neg_rows else None print() print("=" * 60) print("3. 追溯负值会员的上游数据链") print("=" * 60) if neg_member is None: print(" 无负值记录,跳过") else: # 先查 dwd_payment 的 member 相关列 conn.rollback() cur.execute(""" SELECT column_name FROM information_schema.columns WHERE table_schema='dwd' AND table_name='dwd_payment' AND column_name LIKE '%%member%%' """) pay_member_cols = [r['column_name'] for r in cur.fetchall()] print(f" dwd_payment member 列: {pay_member_cols}") # 查 dwd_settlement_head 中该会员的结算记录 cur.execute(""" SELECT column_name FROM information_schema.columns WHERE table_schema='dwd' AND table_name='dwd_settlement_head' AND (column_name LIKE '%%amount%%' OR column_name LIKE '%%total%%') """) settle_amt_cols = [r['column_name'] for r in cur.fetchall()] print(f" dwd_settlement_head amount/total 列: {settle_amt_cols}") # 用实际列名查 if settle_amt_cols: amt_col = settle_amt_cols[0] cur.execute(f""" SELECT COUNT(*) as cnt, SUM({amt_col}) as total, MIN({amt_col}) as min_amt, MAX({amt_col}) as max_amt FROM dwd.dwd_settlement_head WHERE member_id = %s AND site_id = %s """, (neg_member, neg_site)) r = cur.fetchone() print(f" dwd_settlement_head ({amt_col}): {r['cnt']} 条, 总额={r['total']}, min={r['min_amt']}, max={r['max_amt']}") # 查该会员的退款 cur.execute(""" SELECT column_name FROM information_schema.columns WHERE table_schema='dwd' AND table_name='dwd_refund' AND column_name LIKE '%%member%%' """) refund_member_cols = [r['column_name'] for r in cur.fetchall()] print(f" dwd_refund member 列: {refund_member_cols}") # 查 DWS 汇总的完整记录 cur.execute(""" SELECT * FROM dws.dws_member_consumption_summary WHERE member_id = %s AND site_id = %s """, (neg_member, neg_site)) row = cur.fetchone() if row: print(f" dws_member_consumption_summary 完整记录:") for k, v in row.items(): if v is not None and str(v) != '0' and str(v) != '0.00': print(f" {k} = {v}") print() print("=" * 60) print("4. API_SAMPLE_CACHE_ROOT 检查") print("=" * 60) api_cache = os.environ.get("API_SAMPLE_CACHE_ROOT", "NOT SET") print(f" API_SAMPLE_CACHE_ROOT = {api_cache}") if api_cache != "NOT SET": p = Path(api_cache) print(f" exists: {p.exists()}") if p.exists(): files = list(p.glob("*.json")) print(f" json files: {len(files)}") for f in files[:10]: print(f" {f.name}") else: print(" 目录不存在 — FlowRunner 内置检查找不到 API JSON 缓存") print() print("=" * 60) print("5. FETCH_ROOT (ODS JSON 落盘) 检查") print("=" * 60) fetch_root = os.environ.get("FETCH_ROOT", "NOT SET") print(f" FETCH_ROOT = {fetch_root}") if fetch_root != "NOT SET": p = Path(fetch_root) print(f" exists: {p.exists()}") if p.exists(): subdirs = [d for d in p.iterdir() if d.is_dir()] print(f" 子目录数: {len(subdirs)}") for d in sorted(subdirs)[:10]: print(f" {d.name}/") print() print("=" * 60) print("6. FlowRunner 注入 ODS_STAFF_INFO 的来源") print("=" * 60) print(" ODS_STAFF_INFO 在 ENABLED_ODS_CODES 中: 是") print(" ODS_STAFF_INFO 在 task_registry 中: 是 (is_common=True)") print(" 但 FlowRunner 的 api_full flow 可能有自己的任务列表注入逻辑") print(" 需要检查 flow_runner.py 中 api_full 的任务解析") conn.close()